diff options
| author | Tyge Lovset <[email protected]> | 2022-12-20 23:31:51 +0100 |
|---|---|---|
| committer | Tyge Lovset <[email protected]> | 2022-12-20 23:31:51 +0100 |
| commit | 5f57d597cd27aef55adbcb3b452973b0c6e33667 (patch) | |
| tree | dfd59c2fd0e36a6ef37912a9d0cc5a65970f1524 /misc | |
| parent | 1763be8c8cbbc0896477fcf924edd4180d1345a9 (diff) | |
| download | STC-modified-5f57d597cd27aef55adbcb3b452973b0c6e33667.tar.gz STC-modified-5f57d597cd27aef55adbcb3b452973b0c6e33667.zip | |
Restructured folders: examples, benchmarks, tests into misc folder.
Diffstat (limited to 'misc')
113 files changed, 26821 insertions, 0 deletions
diff --git a/misc/benchmarks/build_all.sh b/misc/benchmarks/build_all.sh new file mode 100644 index 00000000..54340998 --- /dev/null +++ b/misc/benchmarks/build_all.sh @@ -0,0 +1,32 @@ +#!/bin/bash +cc='g++ -I../../include -s -O3 -Wall -pedantic -x c++ -std=c++20' +#cc='clang++ -I../include -s -O3 -Wall -pedantic -x c++ -std=c++20' +#cc='cl -nologo -I../include -O2 -TP -EHsc -std:c++20' +run=0 +if [ "$1" == '-h' -o "$1" == '--help' ]; then + echo usage: runall.sh [-run] [compiler + options] + exit +fi +if [ "$1" == '-run' ]; then + run=1 + shift +fi +if [ ! -z "$1" ] ; then + cc=$@ +fi +if [ $run = 0 ] ; then + for i in *.cpp misc/*.c* picobench/*.cpp plotbench/*.cpp ; do + echo $cc -I../include $i -o $(basename -s .cpp $i).exe + $cc -I../include $i -o $(basename -s .cpp $i).exe + done +else + for i in misc/*.c* picobench/*.cpp ; do + echo $cc -O3 -I../include $i + $cc -O3 -I../include $i + if [ -f $(basename -s .c $i).exe ]; then ./$(basename -s .c $i).exe; fi + if [ -f ./a.exe ]; then ./a.exe; fi + if [ -f ./a.out ]; then ./a.out; fi + done +fi + +rm -f a.out *.o *.obj # *.exe diff --git a/misc/benchmarks/external/ankerl/robin_hood.h b/misc/benchmarks/external/ankerl/robin_hood.h new file mode 100644 index 00000000..0af031f5 --- /dev/null +++ b/misc/benchmarks/external/ankerl/robin_hood.h @@ -0,0 +1,2544 @@ +// ______ _____ ______ _________ +// ______________ ___ /_ ___(_)_______ ___ /_ ______ ______ ______ / +// __ ___/_ __ \__ __ \__ / __ __ \ __ __ \_ __ \_ __ \_ __ / +// _ / / /_/ /_ /_/ /_ / _ / / / _ / / // /_/ // /_/ // /_/ / +// /_/ \____/ /_.___/ /_/ /_/ /_/ ________/_/ /_/ \____/ \____/ \__,_/ +// _/_____/ +// +// Fast & memory efficient hashtable based on robin hood hashing for C++11/14/17/20 +// https://github.com/martinus/robin-hood-hashing +// +// Licensed under the MIT License <http://opensource.org/licenses/MIT>. +// SPDX-License-Identifier: MIT +// Copyright (c) 2018-2021 Martin Ankerl <http://martin.ankerl.com> +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#ifndef ROBIN_HOOD_H_INCLUDED +#define ROBIN_HOOD_H_INCLUDED + +// see https://semver.org/ +#define ROBIN_HOOD_VERSION_MAJOR 3 // for incompatible API changes +#define ROBIN_HOOD_VERSION_MINOR 11 // for adding functionality in a backwards-compatible manner +#define ROBIN_HOOD_VERSION_PATCH 5 // for backwards-compatible bug fixes + +#include <algorithm> +#include <cstdlib> +#include <cstring> +#include <functional> +#include <limits> +#include <memory> // only to support hash of smart pointers +#include <stdexcept> +#include <string> +#include <type_traits> +#include <utility> +#if __cplusplus >= 201703L +# include <string_view> +#endif + +// #define ROBIN_HOOD_LOG_ENABLED +#ifdef ROBIN_HOOD_LOG_ENABLED +# include <iostream> +# define ROBIN_HOOD_LOG(...) \ + std::cout << __FUNCTION__ << "@" << __LINE__ << ": " << __VA_ARGS__ << std::endl; +#else +# define ROBIN_HOOD_LOG(x) +#endif + +// #define ROBIN_HOOD_TRACE_ENABLED +#ifdef ROBIN_HOOD_TRACE_ENABLED +# include <iostream> +# define ROBIN_HOOD_TRACE(...) \ + std::cout << __FUNCTION__ << "@" << __LINE__ << ": " << __VA_ARGS__ << std::endl; +#else +# define ROBIN_HOOD_TRACE(x) +#endif + +// #define ROBIN_HOOD_COUNT_ENABLED +#ifdef ROBIN_HOOD_COUNT_ENABLED +# include <iostream> +# define ROBIN_HOOD_COUNT(x) ++counts().x; +namespace robin_hood { +struct Counts { + uint64_t shiftUp{}; + uint64_t shiftDown{}; +}; +inline std::ostream& operator<<(std::ostream& os, Counts const& c) { + return os << c.shiftUp << " shiftUp" << std::endl << c.shiftDown << " shiftDown" << std::endl; +} + +static Counts& counts() { + static Counts counts{}; + return counts; +} +} // namespace robin_hood +#else +# define ROBIN_HOOD_COUNT(x) +#endif + +// all non-argument macros should use this facility. See +// https://www.fluentcpp.com/2019/05/28/better-macros-better-flags/ +#define ROBIN_HOOD(x) ROBIN_HOOD_PRIVATE_DEFINITION_##x() + +// mark unused members with this macro +#define ROBIN_HOOD_UNUSED(identifier) + +// bitness +#if SIZE_MAX == UINT32_MAX +# define ROBIN_HOOD_PRIVATE_DEFINITION_BITNESS() 32 +#elif SIZE_MAX == UINT64_MAX +# define ROBIN_HOOD_PRIVATE_DEFINITION_BITNESS() 64 +#else +# error Unsupported bitness +#endif + +// endianess +#ifdef _MSC_VER +# define ROBIN_HOOD_PRIVATE_DEFINITION_LITTLE_ENDIAN() 1 +# define ROBIN_HOOD_PRIVATE_DEFINITION_BIG_ENDIAN() 0 +#else +# define ROBIN_HOOD_PRIVATE_DEFINITION_LITTLE_ENDIAN() \ + (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) +# define ROBIN_HOOD_PRIVATE_DEFINITION_BIG_ENDIAN() (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +#endif + +// inline +#ifdef _MSC_VER +# define ROBIN_HOOD_PRIVATE_DEFINITION_NOINLINE() __declspec(noinline) +#else +# define ROBIN_HOOD_PRIVATE_DEFINITION_NOINLINE() __attribute__((noinline)) +#endif + +// exceptions +#if !defined(__cpp_exceptions) && !defined(__EXCEPTIONS) && !defined(_CPPUNWIND) +# define ROBIN_HOOD_PRIVATE_DEFINITION_HAS_EXCEPTIONS() 0 +#else +# define ROBIN_HOOD_PRIVATE_DEFINITION_HAS_EXCEPTIONS() 1 +#endif + +// count leading/trailing bits +#if !defined(ROBIN_HOOD_DISABLE_INTRINSICS) +# ifdef _MSC_VER +# if ROBIN_HOOD(BITNESS) == 32 +# define ROBIN_HOOD_PRIVATE_DEFINITION_BITSCANFORWARD() _BitScanForward +# else +# define ROBIN_HOOD_PRIVATE_DEFINITION_BITSCANFORWARD() _BitScanForward64 +# endif +# include <intrin.h> +# pragma intrinsic(ROBIN_HOOD(BITSCANFORWARD)) +# define ROBIN_HOOD_COUNT_TRAILING_ZEROES(x) \ + [](size_t mask) noexcept -> int { \ + unsigned long index; \ + return ROBIN_HOOD(BITSCANFORWARD)(&index, mask) ? static_cast<int>(index) \ + : ROBIN_HOOD(BITNESS); \ + }(x) +# else +# if ROBIN_HOOD(BITNESS) == 32 +# define ROBIN_HOOD_PRIVATE_DEFINITION_CTZ() __builtin_ctzl +# define ROBIN_HOOD_PRIVATE_DEFINITION_CLZ() __builtin_clzl +# else +# define ROBIN_HOOD_PRIVATE_DEFINITION_CTZ() __builtin_ctzll +# define ROBIN_HOOD_PRIVATE_DEFINITION_CLZ() __builtin_clzll +# endif +# define ROBIN_HOOD_COUNT_LEADING_ZEROES(x) ((x) ? ROBIN_HOOD(CLZ)(x) : ROBIN_HOOD(BITNESS)) +# define ROBIN_HOOD_COUNT_TRAILING_ZEROES(x) ((x) ? ROBIN_HOOD(CTZ)(x) : ROBIN_HOOD(BITNESS)) +# endif +#endif + +// fallthrough +#ifndef __has_cpp_attribute // For backwards compatibility +# define __has_cpp_attribute(x) 0 +#endif +#if __has_cpp_attribute(clang::fallthrough) +# define ROBIN_HOOD_PRIVATE_DEFINITION_FALLTHROUGH() [[clang::fallthrough]] +#elif __has_cpp_attribute(gnu::fallthrough) +# define ROBIN_HOOD_PRIVATE_DEFINITION_FALLTHROUGH() [[gnu::fallthrough]] +#else +# define ROBIN_HOOD_PRIVATE_DEFINITION_FALLTHROUGH() +#endif + +// likely/unlikely +#ifdef _MSC_VER +# define ROBIN_HOOD_LIKELY(condition) condition +# define ROBIN_HOOD_UNLIKELY(condition) condition +#else +# define ROBIN_HOOD_LIKELY(condition) __builtin_expect(condition, 1) +# define ROBIN_HOOD_UNLIKELY(condition) __builtin_expect(condition, 0) +#endif + +// detect if native wchar_t type is availiable in MSVC +#ifdef _MSC_VER +# ifdef _NATIVE_WCHAR_T_DEFINED +# define ROBIN_HOOD_PRIVATE_DEFINITION_HAS_NATIVE_WCHART() 1 +# else +# define ROBIN_HOOD_PRIVATE_DEFINITION_HAS_NATIVE_WCHART() 0 +# endif +#else +# define ROBIN_HOOD_PRIVATE_DEFINITION_HAS_NATIVE_WCHART() 1 +#endif + +// detect if MSVC supports the pair(std::piecewise_construct_t,...) consructor being constexpr +#ifdef _MSC_VER +# if _MSC_VER <= 1900 +# define ROBIN_HOOD_PRIVATE_DEFINITION_BROKEN_CONSTEXPR() 1 +# else +# define ROBIN_HOOD_PRIVATE_DEFINITION_BROKEN_CONSTEXPR() 0 +# endif +#else +# define ROBIN_HOOD_PRIVATE_DEFINITION_BROKEN_CONSTEXPR() 0 +#endif + +// workaround missing "is_trivially_copyable" in g++ < 5.0 +// See https://stackoverflow.com/a/31798726/48181 +#if defined(__GNUC__) && __GNUC__ < 5 +# define ROBIN_HOOD_IS_TRIVIALLY_COPYABLE(...) __has_trivial_copy(__VA_ARGS__) +#else +# define ROBIN_HOOD_IS_TRIVIALLY_COPYABLE(...) std::is_trivially_copyable<__VA_ARGS__>::value +#endif + +// helpers for C++ versions, see https://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html +#define ROBIN_HOOD_PRIVATE_DEFINITION_CXX() __cplusplus +#define ROBIN_HOOD_PRIVATE_DEFINITION_CXX98() 199711L +#define ROBIN_HOOD_PRIVATE_DEFINITION_CXX11() 201103L +#define ROBIN_HOOD_PRIVATE_DEFINITION_CXX14() 201402L +#define ROBIN_HOOD_PRIVATE_DEFINITION_CXX17() 201703L + +#if ROBIN_HOOD(CXX) >= ROBIN_HOOD(CXX17) +# define ROBIN_HOOD_PRIVATE_DEFINITION_NODISCARD() [[nodiscard]] +#else +# define ROBIN_HOOD_PRIVATE_DEFINITION_NODISCARD() +#endif + +namespace robin_hood { + +#if ROBIN_HOOD(CXX) >= ROBIN_HOOD(CXX14) +# define ROBIN_HOOD_STD std +#else + +// c++11 compatibility layer +namespace ROBIN_HOOD_STD { +template <class T> +struct alignment_of + : std::integral_constant<std::size_t, alignof(typename std::remove_all_extents<T>::type)> {}; + +template <class T, T... Ints> +class integer_sequence { +public: + using value_type = T; + static_assert(std::is_integral<value_type>::value, "not integral type"); + static constexpr std::size_t size() noexcept { + return sizeof...(Ints); + } +}; +template <std::size_t... Inds> +using index_sequence = integer_sequence<std::size_t, Inds...>; + +namespace detail_ { +template <class T, T Begin, T End, bool> +struct IntSeqImpl { + using TValue = T; + static_assert(std::is_integral<TValue>::value, "not integral type"); + static_assert(Begin >= 0 && Begin < End, "unexpected argument (Begin<0 || Begin<=End)"); + + template <class, class> + struct IntSeqCombiner; + + template <TValue... Inds0, TValue... Inds1> + struct IntSeqCombiner<integer_sequence<TValue, Inds0...>, integer_sequence<TValue, Inds1...>> { + using TResult = integer_sequence<TValue, Inds0..., Inds1...>; + }; + + using TResult = + typename IntSeqCombiner<typename IntSeqImpl<TValue, Begin, Begin + (End - Begin) / 2, + (End - Begin) / 2 == 1>::TResult, + typename IntSeqImpl<TValue, Begin + (End - Begin) / 2, End, + (End - Begin + 1) / 2 == 1>::TResult>::TResult; +}; + +template <class T, T Begin> +struct IntSeqImpl<T, Begin, Begin, false> { + using TValue = T; + static_assert(std::is_integral<TValue>::value, "not integral type"); + static_assert(Begin >= 0, "unexpected argument (Begin<0)"); + using TResult = integer_sequence<TValue>; +}; + +template <class T, T Begin, T End> +struct IntSeqImpl<T, Begin, End, true> { + using TValue = T; + static_assert(std::is_integral<TValue>::value, "not integral type"); + static_assert(Begin >= 0, "unexpected argument (Begin<0)"); + using TResult = integer_sequence<TValue, Begin>; +}; +} // namespace detail_ + +template <class T, T N> +using make_integer_sequence = typename detail_::IntSeqImpl<T, 0, N, (N - 0) == 1>::TResult; + +template <std::size_t N> +using make_index_sequence = make_integer_sequence<std::size_t, N>; + +template <class... T> +using index_sequence_for = make_index_sequence<sizeof...(T)>; + +} // namespace ROBIN_HOOD_STD + +#endif + +namespace detail { + +// make sure we static_cast to the correct type for hash_int +#if ROBIN_HOOD(BITNESS) == 64 +using SizeT = uint64_t; +#else +using SizeT = uint32_t; +#endif + +template <typename T> +T rotr(T x, unsigned k) { + return (x >> k) | (x << (8U * sizeof(T) - k)); +} + +// This cast gets rid of warnings like "cast from 'uint8_t*' {aka 'unsigned char*'} to +// 'uint64_t*' {aka 'long unsigned int*'} increases required alignment of target type". Use with +// care! +template <typename T> +inline T reinterpret_cast_no_cast_align_warning(void* ptr) noexcept { + return reinterpret_cast<T>(ptr); +} + +template <typename T> +inline T reinterpret_cast_no_cast_align_warning(void const* ptr) noexcept { + return reinterpret_cast<T>(ptr); +} + +// make sure this is not inlined as it is slow and dramatically enlarges code, thus making other +// inlinings more difficult. Throws are also generally the slow path. +template <typename E, typename... Args> +[[noreturn]] ROBIN_HOOD(NOINLINE) +#if ROBIN_HOOD(HAS_EXCEPTIONS) + void doThrow(Args&&... args) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) + throw E(std::forward<Args>(args)...); +} +#else + void doThrow(Args&&... ROBIN_HOOD_UNUSED(args) /*unused*/) { + abort(); +} +#endif + +template <typename E, typename T, typename... Args> +T* assertNotNull(T* t, Args&&... args) { + if (ROBIN_HOOD_UNLIKELY(nullptr == t)) { + doThrow<E>(std::forward<Args>(args)...); + } + return t; +} + +template <typename T> +inline T unaligned_load(void const* ptr) noexcept { + // using memcpy so we don't get into unaligned load problems. + // compiler should optimize this very well anyways. + T t; + std::memcpy(&t, ptr, sizeof(T)); + return t; +} + +// Allocates bulks of memory for objects of type T. This deallocates the memory in the destructor, +// and keeps a linked list of the allocated memory around. Overhead per allocation is the size of a +// pointer. +template <typename T, size_t MinNumAllocs = 4, size_t MaxNumAllocs = 256> +class BulkPoolAllocator { +public: + BulkPoolAllocator() noexcept = default; + + // does not copy anything, just creates a new allocator. + BulkPoolAllocator(const BulkPoolAllocator& ROBIN_HOOD_UNUSED(o) /*unused*/) noexcept + : mHead(nullptr) + , mListForFree(nullptr) {} + + BulkPoolAllocator(BulkPoolAllocator&& o) noexcept + : mHead(o.mHead) + , mListForFree(o.mListForFree) { + o.mListForFree = nullptr; + o.mHead = nullptr; + } + + BulkPoolAllocator& operator=(BulkPoolAllocator&& o) noexcept { + reset(); + mHead = o.mHead; + mListForFree = o.mListForFree; + o.mListForFree = nullptr; + o.mHead = nullptr; + return *this; + } + + BulkPoolAllocator& + // NOLINTNEXTLINE(bugprone-unhandled-self-assignment,cert-oop54-cpp) + operator=(const BulkPoolAllocator& ROBIN_HOOD_UNUSED(o) /*unused*/) noexcept { + // does not do anything + return *this; + } + + ~BulkPoolAllocator() noexcept { + reset(); + } + + // Deallocates all allocated memory. + void reset() noexcept { + while (mListForFree) { + T* tmp = *mListForFree; + ROBIN_HOOD_LOG("std::free") + std::free(mListForFree); + mListForFree = reinterpret_cast_no_cast_align_warning<T**>(tmp); + } + mHead = nullptr; + } + + // allocates, but does NOT initialize. Use in-place new constructor, e.g. + // T* obj = pool.allocate(); + // ::new (static_cast<void*>(obj)) T(); + T* allocate() { + T* tmp = mHead; + if (!tmp) { + tmp = performAllocation(); + } + + mHead = *reinterpret_cast_no_cast_align_warning<T**>(tmp); + return tmp; + } + + // does not actually deallocate but puts it in store. + // make sure you have already called the destructor! e.g. with + // obj->~T(); + // pool.deallocate(obj); + void deallocate(T* obj) noexcept { + *reinterpret_cast_no_cast_align_warning<T**>(obj) = mHead; + mHead = obj; + } + + // Adds an already allocated block of memory to the allocator. This allocator is from now on + // responsible for freeing the data (with free()). If the provided data is not large enough to + // make use of, it is immediately freed. Otherwise it is reused and freed in the destructor. + void addOrFree(void* ptr, const size_t numBytes) noexcept { + // calculate number of available elements in ptr + if (numBytes < ALIGNMENT + ALIGNED_SIZE) { + // not enough data for at least one element. Free and return. + ROBIN_HOOD_LOG("std::free") + std::free(ptr); + } else { + ROBIN_HOOD_LOG("add to buffer") + add(ptr, numBytes); + } + } + + void swap(BulkPoolAllocator<T, MinNumAllocs, MaxNumAllocs>& other) noexcept { + using std::swap; + swap(mHead, other.mHead); + swap(mListForFree, other.mListForFree); + } + +private: + // iterates the list of allocated memory to calculate how many to alloc next. + // Recalculating this each time saves us a size_t member. + // This ignores the fact that memory blocks might have been added manually with addOrFree. In + // practice, this should not matter much. + ROBIN_HOOD(NODISCARD) size_t calcNumElementsToAlloc() const noexcept { + auto tmp = mListForFree; + size_t numAllocs = MinNumAllocs; + + while (numAllocs * 2 <= MaxNumAllocs && tmp) { + auto x = reinterpret_cast<T***>(tmp); + tmp = *x; + numAllocs *= 2; + } + + return numAllocs; + } + + // WARNING: Underflow if numBytes < ALIGNMENT! This is guarded in addOrFree(). + void add(void* ptr, const size_t numBytes) noexcept { + const size_t numElements = (numBytes - ALIGNMENT) / ALIGNED_SIZE; + + auto data = reinterpret_cast<T**>(ptr); + + // link free list + auto x = reinterpret_cast<T***>(data); + *x = mListForFree; + mListForFree = data; + + // create linked list for newly allocated data + auto* const headT = + reinterpret_cast_no_cast_align_warning<T*>(reinterpret_cast<char*>(ptr) + ALIGNMENT); + + auto* const head = reinterpret_cast<char*>(headT); + + // Visual Studio compiler automatically unrolls this loop, which is pretty cool + for (size_t i = 0; i < numElements; ++i) { + *reinterpret_cast_no_cast_align_warning<char**>(head + i * ALIGNED_SIZE) = + head + (i + 1) * ALIGNED_SIZE; + } + + // last one points to 0 + *reinterpret_cast_no_cast_align_warning<T**>(head + (numElements - 1) * ALIGNED_SIZE) = + mHead; + mHead = headT; + } + + // Called when no memory is available (mHead == 0). + // Don't inline this slow path. + ROBIN_HOOD(NOINLINE) T* performAllocation() { + size_t const numElementsToAlloc = calcNumElementsToAlloc(); + + // alloc new memory: [prev |T, T, ... T] + size_t const bytes = ALIGNMENT + ALIGNED_SIZE * numElementsToAlloc; + ROBIN_HOOD_LOG("std::malloc " << bytes << " = " << ALIGNMENT << " + " << ALIGNED_SIZE + << " * " << numElementsToAlloc) + add(assertNotNull<std::bad_alloc>(std::malloc(bytes)), bytes); + return mHead; + } + + // enforce byte alignment of the T's +#if ROBIN_HOOD(CXX) >= ROBIN_HOOD(CXX14) + static constexpr size_t ALIGNMENT = + (std::max)(std::alignment_of<T>::value, std::alignment_of<T*>::value); +#else + static const size_t ALIGNMENT = + (ROBIN_HOOD_STD::alignment_of<T>::value > ROBIN_HOOD_STD::alignment_of<T*>::value) + ? ROBIN_HOOD_STD::alignment_of<T>::value + : +ROBIN_HOOD_STD::alignment_of<T*>::value; // the + is for walkarround +#endif + + static constexpr size_t ALIGNED_SIZE = ((sizeof(T) - 1) / ALIGNMENT + 1) * ALIGNMENT; + + static_assert(MinNumAllocs >= 1, "MinNumAllocs"); + static_assert(MaxNumAllocs >= MinNumAllocs, "MaxNumAllocs"); + static_assert(ALIGNED_SIZE >= sizeof(T*), "ALIGNED_SIZE"); + static_assert(0 == (ALIGNED_SIZE % sizeof(T*)), "ALIGNED_SIZE mod"); + static_assert(ALIGNMENT >= sizeof(T*), "ALIGNMENT"); + + T* mHead{nullptr}; + T** mListForFree{nullptr}; +}; + +template <typename T, size_t MinSize, size_t MaxSize, bool IsFlat> +struct NodeAllocator; + +// dummy allocator that does nothing +template <typename T, size_t MinSize, size_t MaxSize> +struct NodeAllocator<T, MinSize, MaxSize, true> { + + // we are not using the data, so just free it. + void addOrFree(void* ptr, size_t ROBIN_HOOD_UNUSED(numBytes) /*unused*/) noexcept { + ROBIN_HOOD_LOG("std::free") + std::free(ptr); + } +}; + +template <typename T, size_t MinSize, size_t MaxSize> +struct NodeAllocator<T, MinSize, MaxSize, false> : public BulkPoolAllocator<T, MinSize, MaxSize> {}; + +// c++14 doesn't have is_nothrow_swappable, and clang++ 6.0.1 doesn't like it either, so I'm making +// my own here. +namespace swappable { +#if ROBIN_HOOD(CXX) < ROBIN_HOOD(CXX17) +using std::swap; +template <typename T> +struct nothrow { + static const bool value = noexcept(swap(std::declval<T&>(), std::declval<T&>())); +}; +#else +template <typename T> +struct nothrow { + static const bool value = std::is_nothrow_swappable<T>::value; +}; +#endif +} // namespace swappable + +} // namespace detail + +struct is_transparent_tag {}; + +// A custom pair implementation is used in the map because std::pair is not is_trivially_copyable, +// which means it would not be allowed to be used in std::memcpy. This struct is copyable, which is +// also tested. +template <typename T1, typename T2> +struct pair { + using first_type = T1; + using second_type = T2; + + template <typename U1 = T1, typename U2 = T2, + typename = typename std::enable_if<std::is_default_constructible<U1>::value && + std::is_default_constructible<U2>::value>::type> + constexpr pair() noexcept(noexcept(U1()) && noexcept(U2())) + : first() + , second() {} + + // pair constructors are explicit so we don't accidentally call this ctor when we don't have to. + explicit constexpr pair(std::pair<T1, T2> const& o) noexcept( + noexcept(T1(std::declval<T1 const&>())) && noexcept(T2(std::declval<T2 const&>()))) + : first(o.first) + , second(o.second) {} + + // pair constructors are explicit so we don't accidentally call this ctor when we don't have to. + explicit constexpr pair(std::pair<T1, T2>&& o) noexcept(noexcept( + T1(std::move(std::declval<T1&&>()))) && noexcept(T2(std::move(std::declval<T2&&>())))) + : first(std::move(o.first)) + , second(std::move(o.second)) {} + + constexpr pair(T1&& a, T2&& b) noexcept(noexcept( + T1(std::move(std::declval<T1&&>()))) && noexcept(T2(std::move(std::declval<T2&&>())))) + : first(std::move(a)) + , second(std::move(b)) {} + + template <typename U1, typename U2> + constexpr pair(U1&& a, U2&& b) noexcept(noexcept(T1(std::forward<U1>( + std::declval<U1&&>()))) && noexcept(T2(std::forward<U2>(std::declval<U2&&>())))) + : first(std::forward<U1>(a)) + , second(std::forward<U2>(b)) {} + + template <typename... U1, typename... U2> + // MSVC 2015 produces error "C2476: ‘constexpr’ constructor does not initialize all members" + // if this constructor is constexpr +#if !ROBIN_HOOD(BROKEN_CONSTEXPR) + constexpr +#endif + pair(std::piecewise_construct_t /*unused*/, std::tuple<U1...> a, + std::tuple<U2...> + b) noexcept(noexcept(pair(std::declval<std::tuple<U1...>&>(), + std::declval<std::tuple<U2...>&>(), + ROBIN_HOOD_STD::index_sequence_for<U1...>(), + ROBIN_HOOD_STD::index_sequence_for<U2...>()))) + : pair(a, b, ROBIN_HOOD_STD::index_sequence_for<U1...>(), + ROBIN_HOOD_STD::index_sequence_for<U2...>()) { + } + + // constructor called from the std::piecewise_construct_t ctor + template <typename... U1, size_t... I1, typename... U2, size_t... I2> + pair(std::tuple<U1...>& a, std::tuple<U2...>& b, ROBIN_HOOD_STD::index_sequence<I1...> /*unused*/, ROBIN_HOOD_STD::index_sequence<I2...> /*unused*/) noexcept( + noexcept(T1(std::forward<U1>(std::get<I1>( + std::declval<std::tuple< + U1...>&>()))...)) && noexcept(T2(std:: + forward<U2>(std::get<I2>( + std::declval<std::tuple<U2...>&>()))...))) + : first(std::forward<U1>(std::get<I1>(a))...) + , second(std::forward<U2>(std::get<I2>(b))...) { + // make visual studio compiler happy about warning about unused a & b. + // Visual studio's pair implementation disables warning 4100. + (void)a; + (void)b; + } + + void swap(pair<T1, T2>& o) noexcept((detail::swappable::nothrow<T1>::value) && + (detail::swappable::nothrow<T2>::value)) { + using std::swap; + swap(first, o.first); + swap(second, o.second); + } + + T1 first; // NOLINT(misc-non-private-member-variables-in-classes) + T2 second; // NOLINT(misc-non-private-member-variables-in-classes) +}; + +template <typename A, typename B> +inline void swap(pair<A, B>& a, pair<A, B>& b) noexcept( + noexcept(std::declval<pair<A, B>&>().swap(std::declval<pair<A, B>&>()))) { + a.swap(b); +} + +template <typename A, typename B> +inline constexpr bool operator==(pair<A, B> const& x, pair<A, B> const& y) { + return (x.first == y.first) && (x.second == y.second); +} +template <typename A, typename B> +inline constexpr bool operator!=(pair<A, B> const& x, pair<A, B> const& y) { + return !(x == y); +} +template <typename A, typename B> +inline constexpr bool operator<(pair<A, B> const& x, pair<A, B> const& y) noexcept(noexcept( + std::declval<A const&>() < std::declval<A const&>()) && noexcept(std::declval<B const&>() < + std::declval<B const&>())) { + return x.first < y.first || (!(y.first < x.first) && x.second < y.second); +} +template <typename A, typename B> +inline constexpr bool operator>(pair<A, B> const& x, pair<A, B> const& y) { + return y < x; +} +template <typename A, typename B> +inline constexpr bool operator<=(pair<A, B> const& x, pair<A, B> const& y) { + return !(x > y); +} +template <typename A, typename B> +inline constexpr bool operator>=(pair<A, B> const& x, pair<A, B> const& y) { + return !(x < y); +} + +inline size_t hash_bytes(void const* ptr, size_t len) noexcept { + static constexpr uint64_t m = UINT64_C(0xc6a4a7935bd1e995); + static constexpr uint64_t seed = UINT64_C(0xe17a1465); + static constexpr unsigned int r = 47; + + auto const* const data64 = static_cast<uint64_t const*>(ptr); + uint64_t h = seed ^ (len * m); + + size_t const n_blocks = len / 8; + for (size_t i = 0; i < n_blocks; ++i) { + auto k = detail::unaligned_load<uint64_t>(data64 + i); + + k *= m; + k ^= k >> r; + k *= m; + + h ^= k; + h *= m; + } + + auto const* const data8 = reinterpret_cast<uint8_t const*>(data64 + n_blocks); + switch (len & 7U) { + case 7: + h ^= static_cast<uint64_t>(data8[6]) << 48U; + ROBIN_HOOD(FALLTHROUGH); // FALLTHROUGH + case 6: + h ^= static_cast<uint64_t>(data8[5]) << 40U; + ROBIN_HOOD(FALLTHROUGH); // FALLTHROUGH + case 5: + h ^= static_cast<uint64_t>(data8[4]) << 32U; + ROBIN_HOOD(FALLTHROUGH); // FALLTHROUGH + case 4: + h ^= static_cast<uint64_t>(data8[3]) << 24U; + ROBIN_HOOD(FALLTHROUGH); // FALLTHROUGH + case 3: + h ^= static_cast<uint64_t>(data8[2]) << 16U; + ROBIN_HOOD(FALLTHROUGH); // FALLTHROUGH + case 2: + h ^= static_cast<uint64_t>(data8[1]) << 8U; + ROBIN_HOOD(FALLTHROUGH); // FALLTHROUGH + case 1: + h ^= static_cast<uint64_t>(data8[0]); + h *= m; + ROBIN_HOOD(FALLTHROUGH); // FALLTHROUGH + default: + break; + } + + h ^= h >> r; + + // not doing the final step here, because this will be done by keyToIdx anyways + // h *= m; + // h ^= h >> r; + return static_cast<size_t>(h); +} + +inline size_t hash_int(uint64_t x) noexcept { + // tried lots of different hashes, let's stick with murmurhash3. It's simple, fast, well tested, + // and doesn't need any special 128bit operations. + x ^= x >> 33U; + x *= UINT64_C(0xff51afd7ed558ccd); + x ^= x >> 33U; + + // not doing the final step here, because this will be done by keyToIdx anyways + // x *= UINT64_C(0xc4ceb9fe1a85ec53); + // x ^= x >> 33U; + return static_cast<size_t>(x); +} + +// A thin wrapper around std::hash, performing an additional simple mixing step of the result. +template <typename T, typename Enable = void> +struct hash : public std::hash<T> { + size_t operator()(T const& obj) const + noexcept(noexcept(std::declval<std::hash<T>>().operator()(std::declval<T const&>()))) { + // call base hash + auto result = std::hash<T>::operator()(obj); + // return mixed of that, to be save against identity has + return hash_int(static_cast<detail::SizeT>(result)); + } +}; + +template <typename CharT> +struct hash<std::basic_string<CharT>> { + size_t operator()(std::basic_string<CharT> const& str) const noexcept { + return hash_bytes(str.data(), sizeof(CharT) * str.size()); + } +}; + +#if ROBIN_HOOD(CXX) >= ROBIN_HOOD(CXX17) +template <typename CharT> +struct hash<std::basic_string_view<CharT>> { + size_t operator()(std::basic_string_view<CharT> const& sv) const noexcept { + return hash_bytes(sv.data(), sizeof(CharT) * sv.size()); + } +}; +#endif + +template <class T> +struct hash<T*> { + size_t operator()(T* ptr) const noexcept { + return hash_int(reinterpret_cast<detail::SizeT>(ptr)); + } +}; + +template <class T> +struct hash<std::unique_ptr<T>> { + size_t operator()(std::unique_ptr<T> const& ptr) const noexcept { + return hash_int(reinterpret_cast<detail::SizeT>(ptr.get())); + } +}; + +template <class T> +struct hash<std::shared_ptr<T>> { + size_t operator()(std::shared_ptr<T> const& ptr) const noexcept { + return hash_int(reinterpret_cast<detail::SizeT>(ptr.get())); + } +}; + +template <typename Enum> +struct hash<Enum, typename std::enable_if<std::is_enum<Enum>::value>::type> { + size_t operator()(Enum e) const noexcept { + using Underlying = typename std::underlying_type<Enum>::type; + return hash<Underlying>{}(static_cast<Underlying>(e)); + } +}; + +#define ROBIN_HOOD_HASH_INT(T) \ + template <> \ + struct hash<T> { \ + size_t operator()(T const& obj) const noexcept { \ + return hash_int(static_cast<uint64_t>(obj)); \ + } \ + } + +#if defined(__GNUC__) && !defined(__clang__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wuseless-cast" +#endif +// see https://en.cppreference.com/w/cpp/utility/hash +ROBIN_HOOD_HASH_INT(bool); +ROBIN_HOOD_HASH_INT(char); +ROBIN_HOOD_HASH_INT(signed char); +ROBIN_HOOD_HASH_INT(unsigned char); +ROBIN_HOOD_HASH_INT(char16_t); +ROBIN_HOOD_HASH_INT(char32_t); +#if ROBIN_HOOD(HAS_NATIVE_WCHART) +ROBIN_HOOD_HASH_INT(wchar_t); +#endif +ROBIN_HOOD_HASH_INT(short); +ROBIN_HOOD_HASH_INT(unsigned short); +ROBIN_HOOD_HASH_INT(int); +ROBIN_HOOD_HASH_INT(unsigned int); +ROBIN_HOOD_HASH_INT(long); +ROBIN_HOOD_HASH_INT(long long); +ROBIN_HOOD_HASH_INT(unsigned long); +ROBIN_HOOD_HASH_INT(unsigned long long); +#if defined(__GNUC__) && !defined(__clang__) +# pragma GCC diagnostic pop +#endif +namespace detail { + +template <typename T> +struct void_type { + using type = void; +}; + +template <typename T, typename = void> +struct has_is_transparent : public std::false_type {}; + +template <typename T> +struct has_is_transparent<T, typename void_type<typename T::is_transparent>::type> + : public std::true_type {}; + +// using wrapper classes for hash and key_equal prevents the diamond problem when the same type +// is used. see https://stackoverflow.com/a/28771920/48181 +template <typename T> +struct WrapHash : public T { + WrapHash() = default; + explicit WrapHash(T const& o) noexcept(noexcept(T(std::declval<T const&>()))) + : T(o) {} +}; + +template <typename T> +struct WrapKeyEqual : public T { + WrapKeyEqual() = default; + explicit WrapKeyEqual(T const& o) noexcept(noexcept(T(std::declval<T const&>()))) + : T(o) {} +}; + +// A highly optimized hashmap implementation, using the Robin Hood algorithm. +// +// In most cases, this map should be usable as a drop-in replacement for std::unordered_map, but +// be about 2x faster in most cases and require much less allocations. +// +// This implementation uses the following memory layout: +// +// [Node, Node, ... Node | info, info, ... infoSentinel ] +// +// * Node: either a DataNode that directly has the std::pair<key, val> as member, +// or a DataNode with a pointer to std::pair<key,val>. Which DataNode representation to use +// depends on how fast the swap() operation is. Heuristically, this is automatically choosen +// based on sizeof(). there are always 2^n Nodes. +// +// * info: Each Node in the map has a corresponding info byte, so there are 2^n info bytes. +// Each byte is initialized to 0, meaning the corresponding Node is empty. Set to 1 means the +// corresponding node contains data. Set to 2 means the corresponding Node is filled, but it +// actually belongs to the previous position and was pushed out because that place is already +// taken. +// +// * infoSentinel: Sentinel byte set to 1, so that iterator's ++ can stop at end() without the +// need for a idx variable. +// +// According to STL, order of templates has effect on throughput. That's why I've moved the +// boolean to the front. +// https://www.reddit.com/r/cpp/comments/ahp6iu/compile_time_binary_size_reductions_and_cs_future/eeguck4/ +template <bool IsFlat, size_t MaxLoadFactor100, typename Key, typename T, typename Hash, + typename KeyEqual> +class Table + : public WrapHash<Hash>, + public WrapKeyEqual<KeyEqual>, + detail::NodeAllocator< + typename std::conditional< + std::is_void<T>::value, Key, + robin_hood::pair<typename std::conditional<IsFlat, Key, Key const>::type, T>>::type, + 4, 16384, IsFlat> { +public: + static constexpr bool is_flat = IsFlat; + static constexpr bool is_map = !std::is_void<T>::value; + static constexpr bool is_set = !is_map; + static constexpr bool is_transparent = + has_is_transparent<Hash>::value && has_is_transparent<KeyEqual>::value; + + using key_type = Key; + using mapped_type = T; + using value_type = typename std::conditional< + is_set, Key, + robin_hood::pair<typename std::conditional<is_flat, Key, Key const>::type, T>>::type; + using size_type = size_t; + using hasher = Hash; + using key_equal = KeyEqual; + using Self = Table<IsFlat, MaxLoadFactor100, key_type, mapped_type, hasher, key_equal>; + +private: + static_assert(MaxLoadFactor100 > 10 && MaxLoadFactor100 < 100, + "MaxLoadFactor100 needs to be >10 && < 100"); + + using WHash = WrapHash<Hash>; + using WKeyEqual = WrapKeyEqual<KeyEqual>; + + // configuration defaults + + // make sure we have 8 elements, needed to quickly rehash mInfo + static constexpr size_t InitialNumElements = sizeof(uint64_t); + static constexpr uint32_t InitialInfoNumBits = 5; + static constexpr uint8_t InitialInfoInc = 1U << InitialInfoNumBits; + static constexpr size_t InfoMask = InitialInfoInc - 1U; + static constexpr uint8_t InitialInfoHashShift = 0; + using DataPool = detail::NodeAllocator<value_type, 4, 16384, IsFlat>; + + // type needs to be wider than uint8_t. + using InfoType = uint32_t; + + // DataNode //////////////////////////////////////////////////////// + + // Primary template for the data node. We have special implementations for small and big + // objects. For large objects it is assumed that swap() is fairly slow, so we allocate these + // on the heap so swap merely swaps a pointer. + template <typename M, bool> + class DataNode {}; + + // Small: just allocate on the stack. + template <typename M> + class DataNode<M, true> final { + public: + template <typename... Args> + explicit DataNode(M& ROBIN_HOOD_UNUSED(map) /*unused*/, Args&&... args) noexcept( + noexcept(value_type(std::forward<Args>(args)...))) + : mData(std::forward<Args>(args)...) {} + + DataNode(M& ROBIN_HOOD_UNUSED(map) /*unused*/, DataNode<M, true>&& n) noexcept( + std::is_nothrow_move_constructible<value_type>::value) + : mData(std::move(n.mData)) {} + + // doesn't do anything + void destroy(M& ROBIN_HOOD_UNUSED(map) /*unused*/) noexcept {} + void destroyDoNotDeallocate() noexcept {} + + value_type const* operator->() const noexcept { + return &mData; + } + value_type* operator->() noexcept { + return &mData; + } + + const value_type& operator*() const noexcept { + return mData; + } + + value_type& operator*() noexcept { + return mData; + } + + template <typename VT = value_type> + ROBIN_HOOD(NODISCARD) + typename std::enable_if<is_map, typename VT::first_type&>::type getFirst() noexcept { + return mData.first; + } + template <typename VT = value_type> + ROBIN_HOOD(NODISCARD) + typename std::enable_if<is_set, VT&>::type getFirst() noexcept { + return mData; + } + + template <typename VT = value_type> + ROBIN_HOOD(NODISCARD) + typename std::enable_if<is_map, typename VT::first_type const&>::type + getFirst() const noexcept { + return mData.first; + } + template <typename VT = value_type> + ROBIN_HOOD(NODISCARD) + typename std::enable_if<is_set, VT const&>::type getFirst() const noexcept { + return mData; + } + + template <typename MT = mapped_type> + ROBIN_HOOD(NODISCARD) + typename std::enable_if<is_map, MT&>::type getSecond() noexcept { + return mData.second; + } + + template <typename MT = mapped_type> + ROBIN_HOOD(NODISCARD) + typename std::enable_if<is_set, MT const&>::type getSecond() const noexcept { + return mData.second; + } + + void swap(DataNode<M, true>& o) noexcept( + noexcept(std::declval<value_type>().swap(std::declval<value_type>()))) { + mData.swap(o.mData); + } + + private: + value_type mData; + }; + + // big object: allocate on heap. + template <typename M> + class DataNode<M, false> { + public: + template <typename... Args> + explicit DataNode(M& map, Args&&... args) + : mData(map.allocate()) { + ::new (static_cast<void*>(mData)) value_type(std::forward<Args>(args)...); + } + + DataNode(M& ROBIN_HOOD_UNUSED(map) /*unused*/, DataNode<M, false>&& n) noexcept + : mData(std::move(n.mData)) {} + + void destroy(M& map) noexcept { + // don't deallocate, just put it into list of datapool. + mData->~value_type(); + map.deallocate(mData); + } + + void destroyDoNotDeallocate() noexcept { + mData->~value_type(); + } + + value_type const* operator->() const noexcept { + return mData; + } + + value_type* operator->() noexcept { + return mData; + } + + const value_type& operator*() const { + return *mData; + } + + value_type& operator*() { + return *mData; + } + + template <typename VT = value_type> + ROBIN_HOOD(NODISCARD) + typename std::enable_if<is_map, typename VT::first_type&>::type getFirst() noexcept { + return mData->first; + } + template <typename VT = value_type> + ROBIN_HOOD(NODISCARD) + typename std::enable_if<is_set, VT&>::type getFirst() noexcept { + return *mData; + } + + template <typename VT = value_type> + ROBIN_HOOD(NODISCARD) + typename std::enable_if<is_map, typename VT::first_type const&>::type + getFirst() const noexcept { + return mData->first; + } + template <typename VT = value_type> + ROBIN_HOOD(NODISCARD) + typename std::enable_if<is_set, VT const&>::type getFirst() const noexcept { + return *mData; + } + + template <typename MT = mapped_type> + ROBIN_HOOD(NODISCARD) + typename std::enable_if<is_map, MT&>::type getSecond() noexcept { + return mData->second; + } + + template <typename MT = mapped_type> + ROBIN_HOOD(NODISCARD) + typename std::enable_if<is_map, MT const&>::type getSecond() const noexcept { + return mData->second; + } + + void swap(DataNode<M, false>& o) noexcept { + using std::swap; + swap(mData, o.mData); + } + + private: + value_type* mData; + }; + + using Node = DataNode<Self, IsFlat>; + + // helpers for insertKeyPrepareEmptySpot: extract first entry (only const required) + ROBIN_HOOD(NODISCARD) key_type const& getFirstConst(Node const& n) const noexcept { + return n.getFirst(); + } + + // in case we have void mapped_type, we are not using a pair, thus we just route k through. + // No need to disable this because it's just not used if not applicable. + ROBIN_HOOD(NODISCARD) key_type const& getFirstConst(key_type const& k) const noexcept { + return k; + } + + // in case we have non-void mapped_type, we have a standard robin_hood::pair + template <typename Q = mapped_type> + ROBIN_HOOD(NODISCARD) + typename std::enable_if<!std::is_void<Q>::value, key_type const&>::type + getFirstConst(value_type const& vt) const noexcept { + return vt.first; + } + + // Cloner ////////////////////////////////////////////////////////// + + template <typename M, bool UseMemcpy> + struct Cloner; + + // fast path: Just copy data, without allocating anything. + template <typename M> + struct Cloner<M, true> { + void operator()(M const& source, M& target) const { + auto const* const src = reinterpret_cast<char const*>(source.mKeyVals); + auto* tgt = reinterpret_cast<char*>(target.mKeyVals); + auto const numElementsWithBuffer = target.calcNumElementsWithBuffer(target.mMask + 1); + std::copy(src, src + target.calcNumBytesTotal(numElementsWithBuffer), tgt); + } + }; + + template <typename M> + struct Cloner<M, false> { + void operator()(M const& s, M& t) const { + auto const numElementsWithBuffer = t.calcNumElementsWithBuffer(t.mMask + 1); + std::copy(s.mInfo, s.mInfo + t.calcNumBytesInfo(numElementsWithBuffer), t.mInfo); + + for (size_t i = 0; i < numElementsWithBuffer; ++i) { + if (t.mInfo[i]) { + ::new (static_cast<void*>(t.mKeyVals + i)) Node(t, *s.mKeyVals[i]); + } + } + } + }; + + // Destroyer /////////////////////////////////////////////////////// + + template <typename M, bool IsFlatAndTrivial> + struct Destroyer {}; + + template <typename M> + struct Destroyer<M, true> { + void nodes(M& m) const noexcept { + m.mNumElements = 0; + } + + void nodesDoNotDeallocate(M& m) const noexcept { + m.mNumElements = 0; + } + }; + + template <typename M> + struct Destroyer<M, false> { + void nodes(M& m) const noexcept { + m.mNumElements = 0; + // clear also resets mInfo to 0, that's sometimes not necessary. + auto const numElementsWithBuffer = m.calcNumElementsWithBuffer(m.mMask + 1); + + for (size_t idx = 0; idx < numElementsWithBuffer; ++idx) { + if (0 != m.mInfo[idx]) { + Node& n = m.mKeyVals[idx]; + n.destroy(m); + n.~Node(); + } + } + } + + void nodesDoNotDeallocate(M& m) const noexcept { + m.mNumElements = 0; + // clear also resets mInfo to 0, that's sometimes not necessary. + auto const numElementsWithBuffer = m.calcNumElementsWithBuffer(m.mMask + 1); + for (size_t idx = 0; idx < numElementsWithBuffer; ++idx) { + if (0 != m.mInfo[idx]) { + Node& n = m.mKeyVals[idx]; + n.destroyDoNotDeallocate(); + n.~Node(); + } + } + } + }; + + // Iter //////////////////////////////////////////////////////////// + + struct fast_forward_tag {}; + + // generic iterator for both const_iterator and iterator. + template <bool IsConst> + // NOLINTNEXTLINE(hicpp-special-member-functions,cppcoreguidelines-special-member-functions) + class Iter { + private: + using NodePtr = typename std::conditional<IsConst, Node const*, Node*>::type; + + public: + using difference_type = std::ptrdiff_t; + using value_type = typename Self::value_type; + using reference = typename std::conditional<IsConst, value_type const&, value_type&>::type; + using pointer = typename std::conditional<IsConst, value_type const*, value_type*>::type; + using iterator_category = std::forward_iterator_tag; + + // default constructed iterator can be compared to itself, but WON'T return true when + // compared to end(). + Iter() = default; + + // Rule of zero: nothing specified. The conversion constructor is only enabled for + // iterator to const_iterator, so it doesn't accidentally work as a copy ctor. + + // Conversion constructor from iterator to const_iterator. + template <bool OtherIsConst, + typename = typename std::enable_if<IsConst && !OtherIsConst>::type> + // NOLINTNEXTLINE(hicpp-explicit-conversions) + Iter(Iter<OtherIsConst> const& other) noexcept + : mKeyVals(other.mKeyVals) + , mInfo(other.mInfo) {} + + Iter(NodePtr valPtr, uint8_t const* infoPtr) noexcept + : mKeyVals(valPtr) + , mInfo(infoPtr) {} + + Iter(NodePtr valPtr, uint8_t const* infoPtr, + fast_forward_tag ROBIN_HOOD_UNUSED(tag) /*unused*/) noexcept + : mKeyVals(valPtr) + , mInfo(infoPtr) { + fastForward(); + } + + template <bool OtherIsConst, + typename = typename std::enable_if<IsConst && !OtherIsConst>::type> + Iter& operator=(Iter<OtherIsConst> const& other) noexcept { + mKeyVals = other.mKeyVals; + mInfo = other.mInfo; + return *this; + } + + // prefix increment. Undefined behavior if we are at end()! + Iter& operator++() noexcept { + mInfo++; + mKeyVals++; + fastForward(); + return *this; + } + + Iter operator++(int) noexcept { + Iter tmp = *this; + ++(*this); + return tmp; + } + + reference operator*() const { + return **mKeyVals; + } + + pointer operator->() const { + return &**mKeyVals; + } + + template <bool O> + bool operator==(Iter<O> const& o) const noexcept { + return mKeyVals == o.mKeyVals; + } + + template <bool O> + bool operator!=(Iter<O> const& o) const noexcept { + return mKeyVals != o.mKeyVals; + } + + private: + // fast forward to the next non-free info byte + // I've tried a few variants that don't depend on intrinsics, but unfortunately they are + // quite a bit slower than this one. So I've reverted that change again. See map_benchmark. + void fastForward() noexcept { + size_t n = 0; + while (0U == (n = detail::unaligned_load<size_t>(mInfo))) { + mInfo += sizeof(size_t); + mKeyVals += sizeof(size_t); + } +#if defined(ROBIN_HOOD_DISABLE_INTRINSICS) + // we know for certain that within the next 8 bytes we'll find a non-zero one. + if (ROBIN_HOOD_UNLIKELY(0U == detail::unaligned_load<uint32_t>(mInfo))) { + mInfo += 4; + mKeyVals += 4; + } + if (ROBIN_HOOD_UNLIKELY(0U == detail::unaligned_load<uint16_t>(mInfo))) { + mInfo += 2; + mKeyVals += 2; + } + if (ROBIN_HOOD_UNLIKELY(0U == *mInfo)) { + mInfo += 1; + mKeyVals += 1; + } +#else +# if ROBIN_HOOD(LITTLE_ENDIAN) + auto inc = ROBIN_HOOD_COUNT_TRAILING_ZEROES(n) / 8; +# else + auto inc = ROBIN_HOOD_COUNT_LEADING_ZEROES(n) / 8; +# endif + mInfo += inc; + mKeyVals += inc; +#endif + } + + friend class Table<IsFlat, MaxLoadFactor100, key_type, mapped_type, hasher, key_equal>; + NodePtr mKeyVals{nullptr}; + uint8_t const* mInfo{nullptr}; + }; + + //////////////////////////////////////////////////////////////////// + + // highly performance relevant code. + // Lower bits are used for indexing into the array (2^n size) + // The upper 1-5 bits need to be a reasonable good hash, to save comparisons. + template <typename HashKey> + void keyToIdx(HashKey&& key, size_t* idx, InfoType* info) const { + // In addition to whatever hash is used, add another mul & shift so we get better hashing. + // This serves as a bad hash prevention, if the given data is + // badly mixed. + auto h = static_cast<uint64_t>(WHash::operator()(key)); + + h *= mHashMultiplier; + h ^= h >> 33U; + + // the lower InitialInfoNumBits are reserved for info. + *info = mInfoInc + static_cast<InfoType>((h & InfoMask) >> mInfoHashShift); + *idx = (static_cast<size_t>(h) >> InitialInfoNumBits) & mMask; + } + + // forwards the index by one, wrapping around at the end + void next(InfoType* info, size_t* idx) const noexcept { + *idx = *idx + 1; + *info += mInfoInc; + } + + void nextWhileLess(InfoType* info, size_t* idx) const noexcept { + // unrolling this by hand did not bring any speedups. + while (*info < mInfo[*idx]) { + next(info, idx); + } + } + + // Shift everything up by one element. Tries to move stuff around. + void + shiftUp(size_t startIdx, + size_t const insertion_idx) noexcept(std::is_nothrow_move_assignable<Node>::value) { + auto idx = startIdx; + ::new (static_cast<void*>(mKeyVals + idx)) Node(std::move(mKeyVals[idx - 1])); + while (--idx != insertion_idx) { + mKeyVals[idx] = std::move(mKeyVals[idx - 1]); + } + + idx = startIdx; + while (idx != insertion_idx) { + ROBIN_HOOD_COUNT(shiftUp) + mInfo[idx] = static_cast<uint8_t>(mInfo[idx - 1] + mInfoInc); + if (ROBIN_HOOD_UNLIKELY(mInfo[idx] + mInfoInc > 0xFF)) { + mMaxNumElementsAllowed = 0; + } + --idx; + } + } + + void shiftDown(size_t idx) noexcept(std::is_nothrow_move_assignable<Node>::value) { + // until we find one that is either empty or has zero offset. + // TODO(martinus) we don't need to move everything, just the last one for the same + // bucket. + mKeyVals[idx].destroy(*this); + + // until we find one that is either empty or has zero offset. + while (mInfo[idx + 1] >= 2 * mInfoInc) { + ROBIN_HOOD_COUNT(shiftDown) + mInfo[idx] = static_cast<uint8_t>(mInfo[idx + 1] - mInfoInc); + mKeyVals[idx] = std::move(mKeyVals[idx + 1]); + ++idx; + } + + mInfo[idx] = 0; + // don't destroy, we've moved it + // mKeyVals[idx].destroy(*this); + mKeyVals[idx].~Node(); + } + + // copy of find(), except that it returns iterator instead of const_iterator. + template <typename Other> + ROBIN_HOOD(NODISCARD) + size_t findIdx(Other const& key) const { + size_t idx{}; + InfoType info{}; + keyToIdx(key, &idx, &info); + + do { + // unrolling this twice gives a bit of a speedup. More unrolling did not help. + if (info == mInfo[idx] && + ROBIN_HOOD_LIKELY(WKeyEqual::operator()(key, mKeyVals[idx].getFirst()))) { + return idx; + } + next(&info, &idx); + if (info == mInfo[idx] && + ROBIN_HOOD_LIKELY(WKeyEqual::operator()(key, mKeyVals[idx].getFirst()))) { + return idx; + } + next(&info, &idx); + } while (info <= mInfo[idx]); + + // nothing found! + return mMask == 0 ? 0 + : static_cast<size_t>(std::distance( + mKeyVals, reinterpret_cast_no_cast_align_warning<Node*>(mInfo))); + } + + void cloneData(const Table& o) { + Cloner<Table, IsFlat && ROBIN_HOOD_IS_TRIVIALLY_COPYABLE(Node)>()(o, *this); + } + + // inserts a keyval that is guaranteed to be new, e.g. when the hashmap is resized. + // @return True on success, false if something went wrong + void insert_move(Node&& keyval) { + // we don't retry, fail if overflowing + // don't need to check max num elements + if (0 == mMaxNumElementsAllowed && !try_increase_info()) { + throwOverflowError(); + } + + size_t idx{}; + InfoType info{}; + keyToIdx(keyval.getFirst(), &idx, &info); + + // skip forward. Use <= because we are certain that the element is not there. + while (info <= mInfo[idx]) { + idx = idx + 1; + info += mInfoInc; + } + + // key not found, so we are now exactly where we want to insert it. + auto const insertion_idx = idx; + auto const insertion_info = static_cast<uint8_t>(info); + if (ROBIN_HOOD_UNLIKELY(insertion_info + mInfoInc > 0xFF)) { + mMaxNumElementsAllowed = 0; + } + + // find an empty spot + while (0 != mInfo[idx]) { + next(&info, &idx); + } + + auto& l = mKeyVals[insertion_idx]; + if (idx == insertion_idx) { + ::new (static_cast<void*>(&l)) Node(std::move(keyval)); + } else { + shiftUp(idx, insertion_idx); + l = std::move(keyval); + } + + // put at empty spot + mInfo[insertion_idx] = insertion_info; + + ++mNumElements; + } + +public: + using iterator = Iter<false>; + using const_iterator = Iter<true>; + + Table() noexcept(noexcept(Hash()) && noexcept(KeyEqual())) + : WHash() + , WKeyEqual() { + ROBIN_HOOD_TRACE(this) + } + + // Creates an empty hash map. Nothing is allocated yet, this happens at the first insert. + // This tremendously speeds up ctor & dtor of a map that never receives an element. The + // penalty is payed at the first insert, and not before. Lookup of this empty map works + // because everybody points to DummyInfoByte::b. parameter bucket_count is dictated by the + // standard, but we can ignore it. + explicit Table( + size_t ROBIN_HOOD_UNUSED(bucket_count) /*unused*/, const Hash& h = Hash{}, + const KeyEqual& equal = KeyEqual{}) noexcept(noexcept(Hash(h)) && noexcept(KeyEqual(equal))) + : WHash(h) + , WKeyEqual(equal) { + ROBIN_HOOD_TRACE(this) + } + + template <typename Iter> + Table(Iter first, Iter last, size_t ROBIN_HOOD_UNUSED(bucket_count) /*unused*/ = 0, + const Hash& h = Hash{}, const KeyEqual& equal = KeyEqual{}) + : WHash(h) + , WKeyEqual(equal) { + ROBIN_HOOD_TRACE(this) + insert(first, last); + } + + Table(std::initializer_list<value_type> initlist, + size_t ROBIN_HOOD_UNUSED(bucket_count) /*unused*/ = 0, const Hash& h = Hash{}, + const KeyEqual& equal = KeyEqual{}) + : WHash(h) + , WKeyEqual(equal) { + ROBIN_HOOD_TRACE(this) + insert(initlist.begin(), initlist.end()); + } + + Table(Table&& o) noexcept + : WHash(std::move(static_cast<WHash&>(o))) + , WKeyEqual(std::move(static_cast<WKeyEqual&>(o))) + , DataPool(std::move(static_cast<DataPool&>(o))) { + ROBIN_HOOD_TRACE(this) + if (o.mMask) { + mHashMultiplier = std::move(o.mHashMultiplier); + mKeyVals = std::move(o.mKeyVals); + mInfo = std::move(o.mInfo); + mNumElements = std::move(o.mNumElements); + mMask = std::move(o.mMask); + mMaxNumElementsAllowed = std::move(o.mMaxNumElementsAllowed); + mInfoInc = std::move(o.mInfoInc); + mInfoHashShift = std::move(o.mInfoHashShift); + // set other's mask to 0 so its destructor won't do anything + o.init(); + } + } + + Table& operator=(Table&& o) noexcept { + ROBIN_HOOD_TRACE(this) + if (&o != this) { + if (o.mMask) { + // only move stuff if the other map actually has some data + destroy(); + mHashMultiplier = std::move(o.mHashMultiplier); + mKeyVals = std::move(o.mKeyVals); + mInfo = std::move(o.mInfo); + mNumElements = std::move(o.mNumElements); + mMask = std::move(o.mMask); + mMaxNumElementsAllowed = std::move(o.mMaxNumElementsAllowed); + mInfoInc = std::move(o.mInfoInc); + mInfoHashShift = std::move(o.mInfoHashShift); + WHash::operator=(std::move(static_cast<WHash&>(o))); + WKeyEqual::operator=(std::move(static_cast<WKeyEqual&>(o))); + DataPool::operator=(std::move(static_cast<DataPool&>(o))); + + o.init(); + + } else { + // nothing in the other map => just clear us. + clear(); + } + } + return *this; + } + + Table(const Table& o) + : WHash(static_cast<const WHash&>(o)) + , WKeyEqual(static_cast<const WKeyEqual&>(o)) + , DataPool(static_cast<const DataPool&>(o)) { + ROBIN_HOOD_TRACE(this) + if (!o.empty()) { + // not empty: create an exact copy. it is also possible to just iterate through all + // elements and insert them, but copying is probably faster. + + auto const numElementsWithBuffer = calcNumElementsWithBuffer(o.mMask + 1); + auto const numBytesTotal = calcNumBytesTotal(numElementsWithBuffer); + + ROBIN_HOOD_LOG("std::malloc " << numBytesTotal << " = calcNumBytesTotal(" + << numElementsWithBuffer << ")") + mHashMultiplier = o.mHashMultiplier; + mKeyVals = static_cast<Node*>( + detail::assertNotNull<std::bad_alloc>(std::malloc(numBytesTotal))); + // no need for calloc because clonData does memcpy + mInfo = reinterpret_cast<uint8_t*>(mKeyVals + numElementsWithBuffer); + mNumElements = o.mNumElements; + mMask = o.mMask; + mMaxNumElementsAllowed = o.mMaxNumElementsAllowed; + mInfoInc = o.mInfoInc; + mInfoHashShift = o.mInfoHashShift; + cloneData(o); + } + } + + // Creates a copy of the given map. Copy constructor of each entry is used. + // Not sure why clang-tidy thinks this doesn't handle self assignment, it does + // NOLINTNEXTLINE(bugprone-unhandled-self-assignment,cert-oop54-cpp) + Table& operator=(Table const& o) { + ROBIN_HOOD_TRACE(this) + if (&o == this) { + // prevent assigning of itself + return *this; + } + + // we keep using the old allocator and not assign the new one, because we want to keep + // the memory available. when it is the same size. + if (o.empty()) { + if (0 == mMask) { + // nothing to do, we are empty too + return *this; + } + + // not empty: destroy what we have there + // clear also resets mInfo to 0, that's sometimes not necessary. + destroy(); + init(); + WHash::operator=(static_cast<const WHash&>(o)); + WKeyEqual::operator=(static_cast<const WKeyEqual&>(o)); + DataPool::operator=(static_cast<DataPool const&>(o)); + + return *this; + } + + // clean up old stuff + Destroyer<Self, IsFlat && std::is_trivially_destructible<Node>::value>{}.nodes(*this); + + if (mMask != o.mMask) { + // no luck: we don't have the same array size allocated, so we need to realloc. + if (0 != mMask) { + // only deallocate if we actually have data! + ROBIN_HOOD_LOG("std::free") + std::free(mKeyVals); + } + + auto const numElementsWithBuffer = calcNumElementsWithBuffer(o.mMask + 1); + auto const numBytesTotal = calcNumBytesTotal(numElementsWithBuffer); + ROBIN_HOOD_LOG("std::malloc " << numBytesTotal << " = calcNumBytesTotal(" + << numElementsWithBuffer << ")") + mKeyVals = static_cast<Node*>( + detail::assertNotNull<std::bad_alloc>(std::malloc(numBytesTotal))); + + // no need for calloc here because cloneData performs a memcpy. + mInfo = reinterpret_cast<uint8_t*>(mKeyVals + numElementsWithBuffer); + // sentinel is set in cloneData + } + WHash::operator=(static_cast<const WHash&>(o)); + WKeyEqual::operator=(static_cast<const WKeyEqual&>(o)); + DataPool::operator=(static_cast<DataPool const&>(o)); + mHashMultiplier = o.mHashMultiplier; + mNumElements = o.mNumElements; + mMask = o.mMask; + mMaxNumElementsAllowed = o.mMaxNumElementsAllowed; + mInfoInc = o.mInfoInc; + mInfoHashShift = o.mInfoHashShift; + cloneData(o); + + return *this; + } + + // Swaps everything between the two maps. + void swap(Table& o) { + ROBIN_HOOD_TRACE(this) + using std::swap; + swap(o, *this); + } + + // Clears all data, without resizing. + void clear() { + ROBIN_HOOD_TRACE(this) + if (empty()) { + // don't do anything! also important because we don't want to write to + // DummyInfoByte::b, even though we would just write 0 to it. + return; + } + + Destroyer<Self, IsFlat && std::is_trivially_destructible<Node>::value>{}.nodes(*this); + + auto const numElementsWithBuffer = calcNumElementsWithBuffer(mMask + 1); + // clear everything, then set the sentinel again + uint8_t const z = 0; + std::fill(mInfo, mInfo + calcNumBytesInfo(numElementsWithBuffer), z); + mInfo[numElementsWithBuffer] = 1; + + mInfoInc = InitialInfoInc; + mInfoHashShift = InitialInfoHashShift; + } + + // Destroys the map and all it's contents. + ~Table() { + ROBIN_HOOD_TRACE(this) + destroy(); + } + + // Checks if both tables contain the same entries. Order is irrelevant. + bool operator==(const Table& other) const { + ROBIN_HOOD_TRACE(this) + if (other.size() != size()) { + return false; + } + for (auto const& otherEntry : other) { + if (!has(otherEntry)) { + return false; + } + } + + return true; + } + + bool operator!=(const Table& other) const { + ROBIN_HOOD_TRACE(this) + return !operator==(other); + } + + template <typename Q = mapped_type> + typename std::enable_if<!std::is_void<Q>::value, Q&>::type operator[](const key_type& key) { + ROBIN_HOOD_TRACE(this) + auto idxAndState = insertKeyPrepareEmptySpot(key); + switch (idxAndState.second) { + case InsertionState::key_found: + break; + + case InsertionState::new_node: + ::new (static_cast<void*>(&mKeyVals[idxAndState.first])) + Node(*this, std::piecewise_construct, std::forward_as_tuple(key), + std::forward_as_tuple()); + break; + + case InsertionState::overwrite_node: + mKeyVals[idxAndState.first] = Node(*this, std::piecewise_construct, + std::forward_as_tuple(key), std::forward_as_tuple()); + break; + + case InsertionState::overflow_error: + throwOverflowError(); + } + + return mKeyVals[idxAndState.first].getSecond(); + } + + template <typename Q = mapped_type> + typename std::enable_if<!std::is_void<Q>::value, Q&>::type operator[](key_type&& key) { + ROBIN_HOOD_TRACE(this) + auto idxAndState = insertKeyPrepareEmptySpot(key); + switch (idxAndState.second) { + case InsertionState::key_found: + break; + + case InsertionState::new_node: + ::new (static_cast<void*>(&mKeyVals[idxAndState.first])) + Node(*this, std::piecewise_construct, std::forward_as_tuple(std::move(key)), + std::forward_as_tuple()); + break; + + case InsertionState::overwrite_node: + mKeyVals[idxAndState.first] = + Node(*this, std::piecewise_construct, std::forward_as_tuple(std::move(key)), + std::forward_as_tuple()); + break; + + case InsertionState::overflow_error: + throwOverflowError(); + } + + return mKeyVals[idxAndState.first].getSecond(); + } + + template <typename Iter> + void insert(Iter first, Iter last) { + for (; first != last; ++first) { + // value_type ctor needed because this might be called with std::pair's + insert(value_type(*first)); + } + } + + void insert(std::initializer_list<value_type> ilist) { + for (auto&& vt : ilist) { + insert(std::move(vt)); + } + } + + template <typename... Args> + std::pair<iterator, bool> emplace(Args&&... args) { + ROBIN_HOOD_TRACE(this) + Node n{*this, std::forward<Args>(args)...}; + auto idxAndState = insertKeyPrepareEmptySpot(getFirstConst(n)); + switch (idxAndState.second) { + case InsertionState::key_found: + n.destroy(*this); + break; + + case InsertionState::new_node: + ::new (static_cast<void*>(&mKeyVals[idxAndState.first])) Node(*this, std::move(n)); + break; + + case InsertionState::overwrite_node: + mKeyVals[idxAndState.first] = std::move(n); + break; + + case InsertionState::overflow_error: + n.destroy(*this); + throwOverflowError(); + break; + } + + return std::make_pair(iterator(mKeyVals + idxAndState.first, mInfo + idxAndState.first), + InsertionState::key_found != idxAndState.second); + } + + template <typename... Args> + iterator emplace_hint(const_iterator position, Args&&... args) { + (void)position; + return emplace(std::forward<Args>(args)...).first; + } + + template <typename... Args> + std::pair<iterator, bool> try_emplace(const key_type& key, Args&&... args) { + return try_emplace_impl(key, std::forward<Args>(args)...); + } + + template <typename... Args> + std::pair<iterator, bool> try_emplace(key_type&& key, Args&&... args) { + return try_emplace_impl(std::move(key), std::forward<Args>(args)...); + } + + template <typename... Args> + iterator try_emplace(const_iterator hint, const key_type& key, Args&&... args) { + (void)hint; + return try_emplace_impl(key, std::forward<Args>(args)...).first; + } + + template <typename... Args> + iterator try_emplace(const_iterator hint, key_type&& key, Args&&... args) { + (void)hint; + return try_emplace_impl(std::move(key), std::forward<Args>(args)...).first; + } + + template <typename Mapped> + std::pair<iterator, bool> insert_or_assign(const key_type& key, Mapped&& obj) { + return insertOrAssignImpl(key, std::forward<Mapped>(obj)); + } + + template <typename Mapped> + std::pair<iterator, bool> insert_or_assign(key_type&& key, Mapped&& obj) { + return insertOrAssignImpl(std::move(key), std::forward<Mapped>(obj)); + } + + template <typename Mapped> + iterator insert_or_assign(const_iterator hint, const key_type& key, Mapped&& obj) { + (void)hint; + return insertOrAssignImpl(key, std::forward<Mapped>(obj)).first; + } + + template <typename Mapped> + iterator insert_or_assign(const_iterator hint, key_type&& key, Mapped&& obj) { + (void)hint; + return insertOrAssignImpl(std::move(key), std::forward<Mapped>(obj)).first; + } + + std::pair<iterator, bool> insert(const value_type& keyval) { + ROBIN_HOOD_TRACE(this) + return emplace(keyval); + } + + iterator insert(const_iterator hint, const value_type& keyval) { + (void)hint; + return emplace(keyval).first; + } + + std::pair<iterator, bool> insert(value_type&& keyval) { + return emplace(std::move(keyval)); + } + + iterator insert(const_iterator hint, value_type&& keyval) { + (void)hint; + return emplace(std::move(keyval)).first; + } + + // Returns 1 if key is found, 0 otherwise. + size_t count(const key_type& key) const { // NOLINT(modernize-use-nodiscard) + ROBIN_HOOD_TRACE(this) + auto kv = mKeyVals + findIdx(key); + if (kv != reinterpret_cast_no_cast_align_warning<Node*>(mInfo)) { + return 1; + } + return 0; + } + + template <typename OtherKey, typename Self_ = Self> + // NOLINTNEXTLINE(modernize-use-nodiscard) + typename std::enable_if<Self_::is_transparent, size_t>::type count(const OtherKey& key) const { + ROBIN_HOOD_TRACE(this) + auto kv = mKeyVals + findIdx(key); + if (kv != reinterpret_cast_no_cast_align_warning<Node*>(mInfo)) { + return 1; + } + return 0; + } + + bool contains(const key_type& key) const { // NOLINT(modernize-use-nodiscard) + return 1U == count(key); + } + + template <typename OtherKey, typename Self_ = Self> + // NOLINTNEXTLINE(modernize-use-nodiscard) + typename std::enable_if<Self_::is_transparent, bool>::type contains(const OtherKey& key) const { + return 1U == count(key); + } + + // Returns a reference to the value found for key. + // Throws std::out_of_range if element cannot be found + template <typename Q = mapped_type> + // NOLINTNEXTLINE(modernize-use-nodiscard) + typename std::enable_if<!std::is_void<Q>::value, Q&>::type at(key_type const& key) { + ROBIN_HOOD_TRACE(this) + auto kv = mKeyVals + findIdx(key); + if (kv == reinterpret_cast_no_cast_align_warning<Node*>(mInfo)) { + doThrow<std::out_of_range>("key not found"); + } + return kv->getSecond(); + } + + // Returns a reference to the value found for key. + // Throws std::out_of_range if element cannot be found + template <typename Q = mapped_type> + // NOLINTNEXTLINE(modernize-use-nodiscard) + typename std::enable_if<!std::is_void<Q>::value, Q const&>::type at(key_type const& key) const { + ROBIN_HOOD_TRACE(this) + auto kv = mKeyVals + findIdx(key); + if (kv == reinterpret_cast_no_cast_align_warning<Node*>(mInfo)) { + doThrow<std::out_of_range>("key not found"); + } + return kv->getSecond(); + } + + const_iterator find(const key_type& key) const { // NOLINT(modernize-use-nodiscard) + ROBIN_HOOD_TRACE(this) + const size_t idx = findIdx(key); + return const_iterator{mKeyVals + idx, mInfo + idx}; + } + + template <typename OtherKey> + const_iterator find(const OtherKey& key, is_transparent_tag /*unused*/) const { + ROBIN_HOOD_TRACE(this) + const size_t idx = findIdx(key); + return const_iterator{mKeyVals + idx, mInfo + idx}; + } + + template <typename OtherKey, typename Self_ = Self> + typename std::enable_if<Self_::is_transparent, // NOLINT(modernize-use-nodiscard) + const_iterator>::type // NOLINT(modernize-use-nodiscard) + find(const OtherKey& key) const { // NOLINT(modernize-use-nodiscard) + ROBIN_HOOD_TRACE(this) + const size_t idx = findIdx(key); + return const_iterator{mKeyVals + idx, mInfo + idx}; + } + + iterator find(const key_type& key) { + ROBIN_HOOD_TRACE(this) + const size_t idx = findIdx(key); + return iterator{mKeyVals + idx, mInfo + idx}; + } + + template <typename OtherKey> + iterator find(const OtherKey& key, is_transparent_tag /*unused*/) { + ROBIN_HOOD_TRACE(this) + const size_t idx = findIdx(key); + return iterator{mKeyVals + idx, mInfo + idx}; + } + + template <typename OtherKey, typename Self_ = Self> + typename std::enable_if<Self_::is_transparent, iterator>::type find(const OtherKey& key) { + ROBIN_HOOD_TRACE(this) + const size_t idx = findIdx(key); + return iterator{mKeyVals + idx, mInfo + idx}; + } + + iterator begin() { + ROBIN_HOOD_TRACE(this) + if (empty()) { + return end(); + } + return iterator(mKeyVals, mInfo, fast_forward_tag{}); + } + const_iterator begin() const { // NOLINT(modernize-use-nodiscard) + ROBIN_HOOD_TRACE(this) + return cbegin(); + } + const_iterator cbegin() const { // NOLINT(modernize-use-nodiscard) + ROBIN_HOOD_TRACE(this) + if (empty()) { + return cend(); + } + return const_iterator(mKeyVals, mInfo, fast_forward_tag{}); + } + + iterator end() { + ROBIN_HOOD_TRACE(this) + // no need to supply valid info pointer: end() must not be dereferenced, and only node + // pointer is compared. + return iterator{reinterpret_cast_no_cast_align_warning<Node*>(mInfo), nullptr}; + } + const_iterator end() const { // NOLINT(modernize-use-nodiscard) + ROBIN_HOOD_TRACE(this) + return cend(); + } + const_iterator cend() const { // NOLINT(modernize-use-nodiscard) + ROBIN_HOOD_TRACE(this) + return const_iterator{reinterpret_cast_no_cast_align_warning<Node*>(mInfo), nullptr}; + } + + iterator erase(const_iterator pos) { + ROBIN_HOOD_TRACE(this) + // its safe to perform const cast here + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) + return erase(iterator{const_cast<Node*>(pos.mKeyVals), const_cast<uint8_t*>(pos.mInfo)}); + } + + // Erases element at pos, returns iterator to the next element. + iterator erase(iterator pos) { + ROBIN_HOOD_TRACE(this) + // we assume that pos always points to a valid entry, and not end(). + auto const idx = static_cast<size_t>(pos.mKeyVals - mKeyVals); + + shiftDown(idx); + --mNumElements; + + if (*pos.mInfo) { + // we've backward shifted, return this again + return pos; + } + + // no backward shift, return next element + return ++pos; + } + + size_t erase(const key_type& key) { + ROBIN_HOOD_TRACE(this) + size_t idx{}; + InfoType info{}; + keyToIdx(key, &idx, &info); + + // check while info matches with the source idx + do { + if (info == mInfo[idx] && WKeyEqual::operator()(key, mKeyVals[idx].getFirst())) { + shiftDown(idx); + --mNumElements; + return 1; + } + next(&info, &idx); + } while (info <= mInfo[idx]); + + // nothing found to delete + return 0; + } + + // reserves space for the specified number of elements. Makes sure the old data fits. + // exactly the same as reserve(c). + void rehash(size_t c) { + // forces a reserve + reserve(c, true); + } + + // reserves space for the specified number of elements. Makes sure the old data fits. + // Exactly the same as rehash(c). Use rehash(0) to shrink to fit. + void reserve(size_t c) { + // reserve, but don't force rehash + reserve(c, false); + } + + // If possible reallocates the map to a smaller one. This frees the underlying table. + // Does not do anything if load_factor is too large for decreasing the table's size. + void compact() { + ROBIN_HOOD_TRACE(this) + auto newSize = InitialNumElements; + while (calcMaxNumElementsAllowed(newSize) < mNumElements && newSize != 0) { + newSize *= 2; + } + if (ROBIN_HOOD_UNLIKELY(newSize == 0)) { + throwOverflowError(); + } + + ROBIN_HOOD_LOG("newSize > mMask + 1: " << newSize << " > " << mMask << " + 1") + + // only actually do anything when the new size is bigger than the old one. This prevents to + // continuously allocate for each reserve() call. + if (newSize < mMask + 1) { + rehashPowerOfTwo(newSize, true); + } + } + + size_type size() const noexcept { // NOLINT(modernize-use-nodiscard) + ROBIN_HOOD_TRACE(this) + return mNumElements; + } + + size_type max_size() const noexcept { // NOLINT(modernize-use-nodiscard) + ROBIN_HOOD_TRACE(this) + return static_cast<size_type>(-1); + } + + ROBIN_HOOD(NODISCARD) bool empty() const noexcept { + ROBIN_HOOD_TRACE(this) + return 0 == mNumElements; + } + + float max_load_factor() const noexcept { // NOLINT(modernize-use-nodiscard) + ROBIN_HOOD_TRACE(this) + return MaxLoadFactor100 / 100.0F; + } + + // Average number of elements per bucket. Since we allow only 1 per bucket + float load_factor() const noexcept { // NOLINT(modernize-use-nodiscard) + ROBIN_HOOD_TRACE(this) + return static_cast<float>(size()) / static_cast<float>(mMask + 1); + } + + ROBIN_HOOD(NODISCARD) size_t mask() const noexcept { + ROBIN_HOOD_TRACE(this) + return mMask; + } + + ROBIN_HOOD(NODISCARD) size_t calcMaxNumElementsAllowed(size_t maxElements) const noexcept { + if (ROBIN_HOOD_LIKELY(maxElements <= (std::numeric_limits<size_t>::max)() / 100)) { + return maxElements * MaxLoadFactor100 / 100; + } + + // we might be a bit inprecise, but since maxElements is quite large that doesn't matter + return (maxElements / 100) * MaxLoadFactor100; + } + + ROBIN_HOOD(NODISCARD) size_t calcNumBytesInfo(size_t numElements) const noexcept { + // we add a uint64_t, which houses the sentinel (first byte) and padding so we can load + // 64bit types. + return numElements + sizeof(uint64_t); + } + + ROBIN_HOOD(NODISCARD) + size_t calcNumElementsWithBuffer(size_t numElements) const noexcept { + auto maxNumElementsAllowed = calcMaxNumElementsAllowed(numElements); + return numElements + (std::min)(maxNumElementsAllowed, (static_cast<size_t>(0xFF))); + } + + // calculation only allowed for 2^n values + ROBIN_HOOD(NODISCARD) size_t calcNumBytesTotal(size_t numElements) const { +#if ROBIN_HOOD(BITNESS) == 64 + return numElements * sizeof(Node) + calcNumBytesInfo(numElements); +#else + // make sure we're doing 64bit operations, so we are at least safe against 32bit overflows. + auto const ne = static_cast<uint64_t>(numElements); + auto const s = static_cast<uint64_t>(sizeof(Node)); + auto const infos = static_cast<uint64_t>(calcNumBytesInfo(numElements)); + + auto const total64 = ne * s + infos; + auto const total = static_cast<size_t>(total64); + + if (ROBIN_HOOD_UNLIKELY(static_cast<uint64_t>(total) != total64)) { + throwOverflowError(); + } + return total; +#endif + } + +private: + template <typename Q = mapped_type> + ROBIN_HOOD(NODISCARD) + typename std::enable_if<!std::is_void<Q>::value, bool>::type has(const value_type& e) const { + ROBIN_HOOD_TRACE(this) + auto it = find(e.first); + return it != end() && it->second == e.second; + } + + template <typename Q = mapped_type> + ROBIN_HOOD(NODISCARD) + typename std::enable_if<std::is_void<Q>::value, bool>::type has(const value_type& e) const { + ROBIN_HOOD_TRACE(this) + return find(e) != end(); + } + + void reserve(size_t c, bool forceRehash) { + ROBIN_HOOD_TRACE(this) + auto const minElementsAllowed = (std::max)(c, mNumElements); + auto newSize = InitialNumElements; + while (calcMaxNumElementsAllowed(newSize) < minElementsAllowed && newSize != 0) { + newSize *= 2; + } + if (ROBIN_HOOD_UNLIKELY(newSize == 0)) { + throwOverflowError(); + } + + ROBIN_HOOD_LOG("newSize > mMask + 1: " << newSize << " > " << mMask << " + 1") + + // only actually do anything when the new size is bigger than the old one. This prevents to + // continuously allocate for each reserve() call. + if (forceRehash || newSize > mMask + 1) { + rehashPowerOfTwo(newSize, false); + } + } + + // reserves space for at least the specified number of elements. + // only works if numBuckets if power of two + // True on success, false otherwise + void rehashPowerOfTwo(size_t numBuckets, bool forceFree) { + ROBIN_HOOD_TRACE(this) + + Node* const oldKeyVals = mKeyVals; + uint8_t const* const oldInfo = mInfo; + + const size_t oldMaxElementsWithBuffer = calcNumElementsWithBuffer(mMask + 1); + + // resize operation: move stuff + initData(numBuckets); + if (oldMaxElementsWithBuffer > 1) { + for (size_t i = 0; i < oldMaxElementsWithBuffer; ++i) { + if (oldInfo[i] != 0) { + // might throw an exception, which is really bad since we are in the middle of + // moving stuff. + insert_move(std::move(oldKeyVals[i])); + // destroy the node but DON'T destroy the data. + oldKeyVals[i].~Node(); + } + } + + // this check is not necessary as it's guarded by the previous if, but it helps + // silence g++'s overeager "attempt to free a non-heap object 'map' + // [-Werror=free-nonheap-object]" warning. + if (oldKeyVals != reinterpret_cast_no_cast_align_warning<Node*>(&mMask)) { + // don't destroy old data: put it into the pool instead + if (forceFree) { + std::free(oldKeyVals); + } else { + DataPool::addOrFree(oldKeyVals, calcNumBytesTotal(oldMaxElementsWithBuffer)); + } + } + } + } + + ROBIN_HOOD(NOINLINE) void throwOverflowError() const { +#if ROBIN_HOOD(HAS_EXCEPTIONS) + throw std::overflow_error("robin_hood::map overflow"); +#else + abort(); +#endif + } + + template <typename OtherKey, typename... Args> + std::pair<iterator, bool> try_emplace_impl(OtherKey&& key, Args&&... args) { + ROBIN_HOOD_TRACE(this) + auto idxAndState = insertKeyPrepareEmptySpot(key); + switch (idxAndState.second) { + case InsertionState::key_found: + break; + + case InsertionState::new_node: + ::new (static_cast<void*>(&mKeyVals[idxAndState.first])) Node( + *this, std::piecewise_construct, std::forward_as_tuple(std::forward<OtherKey>(key)), + std::forward_as_tuple(std::forward<Args>(args)...)); + break; + + case InsertionState::overwrite_node: + mKeyVals[idxAndState.first] = Node(*this, std::piecewise_construct, + std::forward_as_tuple(std::forward<OtherKey>(key)), + std::forward_as_tuple(std::forward<Args>(args)...)); + break; + + case InsertionState::overflow_error: + throwOverflowError(); + break; + } + + return std::make_pair(iterator(mKeyVals + idxAndState.first, mInfo + idxAndState.first), + InsertionState::key_found != idxAndState.second); + } + + template <typename OtherKey, typename Mapped> + std::pair<iterator, bool> insertOrAssignImpl(OtherKey&& key, Mapped&& obj) { + ROBIN_HOOD_TRACE(this) + auto idxAndState = insertKeyPrepareEmptySpot(key); + switch (idxAndState.second) { + case InsertionState::key_found: + mKeyVals[idxAndState.first].getSecond() = std::forward<Mapped>(obj); + break; + + case InsertionState::new_node: + ::new (static_cast<void*>(&mKeyVals[idxAndState.first])) Node( + *this, std::piecewise_construct, std::forward_as_tuple(std::forward<OtherKey>(key)), + std::forward_as_tuple(std::forward<Mapped>(obj))); + break; + + case InsertionState::overwrite_node: + mKeyVals[idxAndState.first] = Node(*this, std::piecewise_construct, + std::forward_as_tuple(std::forward<OtherKey>(key)), + std::forward_as_tuple(std::forward<Mapped>(obj))); + break; + + case InsertionState::overflow_error: + throwOverflowError(); + break; + } + + return std::make_pair(iterator(mKeyVals + idxAndState.first, mInfo + idxAndState.first), + InsertionState::key_found != idxAndState.second); + } + + void initData(size_t max_elements) { + mNumElements = 0; + mMask = max_elements - 1; + mMaxNumElementsAllowed = calcMaxNumElementsAllowed(max_elements); + + auto const numElementsWithBuffer = calcNumElementsWithBuffer(max_elements); + + // malloc & zero mInfo. Faster than calloc everything. + auto const numBytesTotal = calcNumBytesTotal(numElementsWithBuffer); + ROBIN_HOOD_LOG("std::calloc " << numBytesTotal << " = calcNumBytesTotal(" + << numElementsWithBuffer << ")") + mKeyVals = reinterpret_cast<Node*>( + detail::assertNotNull<std::bad_alloc>(std::malloc(numBytesTotal))); + mInfo = reinterpret_cast<uint8_t*>(mKeyVals + numElementsWithBuffer); + std::memset(mInfo, 0, numBytesTotal - numElementsWithBuffer * sizeof(Node)); + + // set sentinel + mInfo[numElementsWithBuffer] = 1; + + mInfoInc = InitialInfoInc; + mInfoHashShift = InitialInfoHashShift; + } + + enum class InsertionState { overflow_error, key_found, new_node, overwrite_node }; + + // Finds key, and if not already present prepares a spot where to pot the key & value. + // This potentially shifts nodes out of the way, updates mInfo and number of inserted + // elements, so the only operation left to do is create/assign a new node at that spot. + template <typename OtherKey> + std::pair<size_t, InsertionState> insertKeyPrepareEmptySpot(OtherKey&& key) { + for (int i = 0; i < 256; ++i) { + size_t idx{}; + InfoType info{}; + keyToIdx(key, &idx, &info); + nextWhileLess(&info, &idx); + + // while we potentially have a match + while (info == mInfo[idx]) { + if (WKeyEqual::operator()(key, mKeyVals[idx].getFirst())) { + // key already exists, do NOT insert. + // see http://en.cppreference.com/w/cpp/container/unordered_map/insert + return std::make_pair(idx, InsertionState::key_found); + } + next(&info, &idx); + } + + // unlikely that this evaluates to true + if (ROBIN_HOOD_UNLIKELY(mNumElements >= mMaxNumElementsAllowed)) { + if (!increase_size()) { + return std::make_pair(size_t(0), InsertionState::overflow_error); + } + continue; + } + + // key not found, so we are now exactly where we want to insert it. + auto const insertion_idx = idx; + auto const insertion_info = info; + if (ROBIN_HOOD_UNLIKELY(insertion_info + mInfoInc > 0xFF)) { + mMaxNumElementsAllowed = 0; + } + + // find an empty spot + while (0 != mInfo[idx]) { + next(&info, &idx); + } + + if (idx != insertion_idx) { + shiftUp(idx, insertion_idx); + } + // put at empty spot + mInfo[insertion_idx] = static_cast<uint8_t>(insertion_info); + ++mNumElements; + return std::make_pair(insertion_idx, idx == insertion_idx + ? InsertionState::new_node + : InsertionState::overwrite_node); + } + + // enough attempts failed, so finally give up. + return std::make_pair(size_t(0), InsertionState::overflow_error); + } + + bool try_increase_info() { + ROBIN_HOOD_LOG("mInfoInc=" << mInfoInc << ", numElements=" << mNumElements + << ", maxNumElementsAllowed=" + << calcMaxNumElementsAllowed(mMask + 1)) + if (mInfoInc <= 2) { + // need to be > 2 so that shift works (otherwise undefined behavior!) + return false; + } + // we got space left, try to make info smaller + mInfoInc = static_cast<uint8_t>(mInfoInc >> 1U); + + // remove one bit of the hash, leaving more space for the distance info. + // This is extremely fast because we can operate on 8 bytes at once. + ++mInfoHashShift; + auto const numElementsWithBuffer = calcNumElementsWithBuffer(mMask + 1); + + for (size_t i = 0; i < numElementsWithBuffer; i += 8) { + auto val = unaligned_load<uint64_t>(mInfo + i); + val = (val >> 1U) & UINT64_C(0x7f7f7f7f7f7f7f7f); + std::memcpy(mInfo + i, &val, sizeof(val)); + } + // update sentinel, which might have been cleared out! + mInfo[numElementsWithBuffer] = 1; + + mMaxNumElementsAllowed = calcMaxNumElementsAllowed(mMask + 1); + return true; + } + + // True if resize was possible, false otherwise + bool increase_size() { + // nothing allocated yet? just allocate InitialNumElements + if (0 == mMask) { + initData(InitialNumElements); + return true; + } + + auto const maxNumElementsAllowed = calcMaxNumElementsAllowed(mMask + 1); + if (mNumElements < maxNumElementsAllowed && try_increase_info()) { + return true; + } + + ROBIN_HOOD_LOG("mNumElements=" << mNumElements << ", maxNumElementsAllowed=" + << maxNumElementsAllowed << ", load=" + << (static_cast<double>(mNumElements) * 100.0 / + (static_cast<double>(mMask) + 1))) + + if (mNumElements * 2 < calcMaxNumElementsAllowed(mMask + 1)) { + // we have to resize, even though there would still be plenty of space left! + // Try to rehash instead. Delete freed memory so we don't steadyily increase mem in case + // we have to rehash a few times + nextHashMultiplier(); + rehashPowerOfTwo(mMask + 1, true); + } else { + // we've reached the capacity of the map, so the hash seems to work nice. Keep using it. + rehashPowerOfTwo((mMask + 1) * 2, false); + } + return true; + } + + void nextHashMultiplier() { + // adding an *even* number, so that the multiplier will always stay odd. This is necessary + // so that the hash stays a mixing function (and thus doesn't have any information loss). + mHashMultiplier += UINT64_C(0xc4ceb9fe1a85ec54); + } + + void destroy() { + if (0 == mMask) { + // don't deallocate! + return; + } + + Destroyer<Self, IsFlat && std::is_trivially_destructible<Node>::value>{} + .nodesDoNotDeallocate(*this); + + // This protection against not deleting mMask shouldn't be needed as it's sufficiently + // protected with the 0==mMask check, but I have this anyways because g++ 7 otherwise + // reports a compile error: attempt to free a non-heap object 'fm' + // [-Werror=free-nonheap-object] + if (mKeyVals != reinterpret_cast_no_cast_align_warning<Node*>(&mMask)) { + ROBIN_HOOD_LOG("std::free") + std::free(mKeyVals); + } + } + + void init() noexcept { + mKeyVals = reinterpret_cast_no_cast_align_warning<Node*>(&mMask); + mInfo = reinterpret_cast<uint8_t*>(&mMask); + mNumElements = 0; + mMask = 0; + mMaxNumElementsAllowed = 0; + mInfoInc = InitialInfoInc; + mInfoHashShift = InitialInfoHashShift; + } + + // members are sorted so no padding occurs + uint64_t mHashMultiplier = UINT64_C(0xc4ceb9fe1a85ec53); // 8 byte 8 + Node* mKeyVals = reinterpret_cast_no_cast_align_warning<Node*>(&mMask); // 8 byte 16 + uint8_t* mInfo = reinterpret_cast<uint8_t*>(&mMask); // 8 byte 24 + size_t mNumElements = 0; // 8 byte 32 + size_t mMask = 0; // 8 byte 40 + size_t mMaxNumElementsAllowed = 0; // 8 byte 48 + InfoType mInfoInc = InitialInfoInc; // 4 byte 52 + InfoType mInfoHashShift = InitialInfoHashShift; // 4 byte 56 + // 16 byte 56 if NodeAllocator +}; + +} // namespace detail + +// map + +template <typename Key, typename T, typename Hash = hash<Key>, + typename KeyEqual = std::equal_to<Key>, size_t MaxLoadFactor100 = 80> +using unordered_flat_map = detail::Table<true, MaxLoadFactor100, Key, T, Hash, KeyEqual>; + +template <typename Key, typename T, typename Hash = hash<Key>, + typename KeyEqual = std::equal_to<Key>, size_t MaxLoadFactor100 = 80> +using unordered_node_map = detail::Table<false, MaxLoadFactor100, Key, T, Hash, KeyEqual>; + +template <typename Key, typename T, typename Hash = hash<Key>, + typename KeyEqual = std::equal_to<Key>, size_t MaxLoadFactor100 = 80> +using unordered_map = + detail::Table<sizeof(robin_hood::pair<Key, T>) <= sizeof(size_t) * 6 && + std::is_nothrow_move_constructible<robin_hood::pair<Key, T>>::value && + std::is_nothrow_move_assignable<robin_hood::pair<Key, T>>::value, + MaxLoadFactor100, Key, T, Hash, KeyEqual>; + +// set + +template <typename Key, typename Hash = hash<Key>, typename KeyEqual = std::equal_to<Key>, + size_t MaxLoadFactor100 = 80> +using unordered_flat_set = detail::Table<true, MaxLoadFactor100, Key, void, Hash, KeyEqual>; + +template <typename Key, typename Hash = hash<Key>, typename KeyEqual = std::equal_to<Key>, + size_t MaxLoadFactor100 = 80> +using unordered_node_set = detail::Table<false, MaxLoadFactor100, Key, void, Hash, KeyEqual>; + +template <typename Key, typename Hash = hash<Key>, typename KeyEqual = std::equal_to<Key>, + size_t MaxLoadFactor100 = 80> +using unordered_set = detail::Table<sizeof(Key) <= sizeof(size_t) * 6 && + std::is_nothrow_move_constructible<Key>::value && + std::is_nothrow_move_assignable<Key>::value, + MaxLoadFactor100, Key, void, Hash, KeyEqual>; + +} // namespace robin_hood + +#endif diff --git a/misc/benchmarks/external/ankerl/unordered_dense.h b/misc/benchmarks/external/ankerl/unordered_dense.h new file mode 100644 index 00000000..ff902ad4 --- /dev/null +++ b/misc/benchmarks/external/ankerl/unordered_dense.h @@ -0,0 +1,1503 @@ +///////////////////////// ankerl::unordered_dense::{map, set} ///////////////////////// + +// A fast & densely stored hashmap and hashset based on robin-hood backward shift deletion. +// Version 2.0.0 +// https://github.com/martinus/unordered_dense +// +// Licensed under the MIT License <http://opensource.org/licenses/MIT>. +// SPDX-License-Identifier: MIT +// Copyright (c) 2022 Martin Leitner-Ankerl <[email protected]> +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#ifndef ANKERL_UNORDERED_DENSE_H +#define ANKERL_UNORDERED_DENSE_H + +// see https://semver.org/spec/v2.0.0.html +#define ANKERL_UNORDERED_DENSE_VERSION_MAJOR 2 // NOLINT(cppcoreguidelines-macro-usage) incompatible API changes +#define ANKERL_UNORDERED_DENSE_VERSION_MINOR 0 // NOLINT(cppcoreguidelines-macro-usage) backwards compatible functionality +#define ANKERL_UNORDERED_DENSE_VERSION_PATCH 0 // NOLINT(cppcoreguidelines-macro-usage) backwards compatible bug fixes + +// API versioning with inline namespace, see https://www.foonathan.net/2018/11/inline-namespaces/ +#define ANKERL_UNORDERED_DENSE_VERSION_CONCAT1(major, minor, patch) v##major##_##minor##_##patch +#define ANKERL_UNORDERED_DENSE_VERSION_CONCAT(major, minor, patch) ANKERL_UNORDERED_DENSE_VERSION_CONCAT1(major, minor, patch) +#define ANKERL_UNORDERED_DENSE_NAMESPACE \ + ANKERL_UNORDERED_DENSE_VERSION_CONCAT( \ + ANKERL_UNORDERED_DENSE_VERSION_MAJOR, ANKERL_UNORDERED_DENSE_VERSION_MINOR, ANKERL_UNORDERED_DENSE_VERSION_PATCH) + +#if defined(_MSVC_LANG) +# define ANKERL_UNORDERED_DENSE_CPP_VERSION _MSVC_LANG +#else +# define ANKERL_UNORDERED_DENSE_CPP_VERSION __cplusplus +#endif + +#if defined(__GNUC__) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +# define ANKERL_UNORDERED_DENSE_PACK(decl) decl __attribute__((__packed__)) +#elif defined(_MSC_VER) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +# define ANKERL_UNORDERED_DENSE_PACK(decl) __pragma(pack(push, 1)) decl __pragma(pack(pop)) +#endif + +#if ANKERL_UNORDERED_DENSE_CPP_VERSION < 201703L +# error ankerl::unordered_dense requires C++17 or higher +#else +# include <array> // for array +# include <cstdint> // for uint64_t, uint32_t, uint8_t, UINT64_C +# include <cstring> // for size_t, memcpy, memset +# include <functional> // for equal_to, hash +# include <initializer_list> // for initializer_list +# include <iterator> // for pair, distance +# include <limits> // for numeric_limits +# include <memory> // for allocator, allocator_traits, shared_ptr +# include <stdexcept> // for out_of_range +# include <string> // for basic_string +# include <string_view> // for basic_string_view, hash +# include <tuple> // for forward_as_tuple +# include <type_traits> // for enable_if_t, declval, conditional_t, ena... +# include <utility> // for forward, exchange, pair, as_const, piece... +# include <vector> // for vector + +# define ANKERL_UNORDERED_DENSE_PMR 0 // NOLINT(cppcoreguidelines-macro-usage) +# if defined(__has_include) +# if __has_include(<memory_resource>) +# undef ANKERL_UNORDERED_DENSE_PMR +# define ANKERL_UNORDERED_DENSE_PMR 1 // NOLINT(cppcoreguidelines-macro-usage) +# include <memory_resource> // for polymorphic_allocator +# endif +# endif + +# if defined(_MSC_VER) && defined(_M_X64) +# include <intrin.h> +# pragma intrinsic(_umul128) +# endif + +# if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__) +# define ANKERL_UNORDERED_DENSE_LIKELY(x) __builtin_expect(x, 1) // NOLINT(cppcoreguidelines-macro-usage) +# define ANKERL_UNORDERED_DENSE_UNLIKELY(x) __builtin_expect(x, 0) // NOLINT(cppcoreguidelines-macro-usage) +# else +# define ANKERL_UNORDERED_DENSE_LIKELY(x) (x) // NOLINT(cppcoreguidelines-macro-usage) +# define ANKERL_UNORDERED_DENSE_UNLIKELY(x) (x) // NOLINT(cppcoreguidelines-macro-usage) +# endif + +namespace ankerl::unordered_dense { +inline namespace ANKERL_UNORDERED_DENSE_NAMESPACE { + +// hash /////////////////////////////////////////////////////////////////////// + +// This is a stripped-down implementation of wyhash: https://github.com/wangyi-fudan/wyhash +// No big-endian support (because different values on different machines don't matter), +// hardcodes seed and the secret, reformattes the code, and clang-tidy fixes. +namespace detail::wyhash { + +static inline void mum(uint64_t* a, uint64_t* b) { +# if defined(__SIZEOF_INT128__) + __uint128_t r = *a; + r *= *b; + *a = static_cast<uint64_t>(r); + *b = static_cast<uint64_t>(r >> 64U); +# elif defined(_MSC_VER) && defined(_M_X64) + *a = _umul128(*a, *b, b); +# else + uint64_t ha = *a >> 32U; + uint64_t hb = *b >> 32U; + uint64_t la = static_cast<uint32_t>(*a); + uint64_t lb = static_cast<uint32_t>(*b); + uint64_t hi{}; + uint64_t lo{}; + uint64_t rh = ha * hb; + uint64_t rm0 = ha * lb; + uint64_t rm1 = hb * la; + uint64_t rl = la * lb; + uint64_t t = rl + (rm0 << 32U); + auto c = static_cast<uint64_t>(t < rl); + lo = t + (rm1 << 32U); + c += static_cast<uint64_t>(lo < t); + hi = rh + (rm0 >> 32U) + (rm1 >> 32U) + c; + *a = lo; + *b = hi; +# endif +} + +// multiply and xor mix function, aka MUM +[[nodiscard]] static inline auto mix(uint64_t a, uint64_t b) -> uint64_t { + mum(&a, &b); + return a ^ b; +} + +// read functions. WARNING: we don't care about endianness, so results are different on big endian! +[[nodiscard]] static inline auto r8(const uint8_t* p) -> uint64_t { + uint64_t v{}; + std::memcpy(&v, p, 8U); + return v; +} + +[[nodiscard]] static inline auto r4(const uint8_t* p) -> uint64_t { + uint32_t v{}; + std::memcpy(&v, p, 4); + return v; +} + +// reads 1, 2, or 3 bytes +[[nodiscard]] static inline auto r3(const uint8_t* p, size_t k) -> uint64_t { + return (static_cast<uint64_t>(p[0]) << 16U) | (static_cast<uint64_t>(p[k >> 1U]) << 8U) | p[k - 1]; +} + +[[maybe_unused]] [[nodiscard]] static inline auto hash(void const* key, size_t len) -> uint64_t { + static constexpr auto secret = std::array{UINT64_C(0xa0761d6478bd642f), + UINT64_C(0xe7037ed1a0b428db), + UINT64_C(0x8ebc6af09c88c6e3), + UINT64_C(0x589965cc75374cc3)}; + + auto const* p = static_cast<uint8_t const*>(key); + uint64_t seed = secret[0]; + uint64_t a{}; + uint64_t b{}; + if (ANKERL_UNORDERED_DENSE_LIKELY(len <= 16)) { + if (ANKERL_UNORDERED_DENSE_LIKELY(len >= 4)) { + a = (r4(p) << 32U) | r4(p + ((len >> 3U) << 2U)); + b = (r4(p + len - 4) << 32U) | r4(p + len - 4 - ((len >> 3U) << 2U)); + } else if (ANKERL_UNORDERED_DENSE_LIKELY(len > 0)) { + a = r3(p, len); + b = 0; + } else { + a = 0; + b = 0; + } + } else { + size_t i = len; + if (ANKERL_UNORDERED_DENSE_UNLIKELY(i > 48)) { + uint64_t see1 = seed; + uint64_t see2 = seed; + do { + seed = mix(r8(p) ^ secret[1], r8(p + 8) ^ seed); + see1 = mix(r8(p + 16) ^ secret[2], r8(p + 24) ^ see1); + see2 = mix(r8(p + 32) ^ secret[3], r8(p + 40) ^ see2); + p += 48; + i -= 48; + } while (ANKERL_UNORDERED_DENSE_LIKELY(i > 48)); + seed ^= see1 ^ see2; + } + while (ANKERL_UNORDERED_DENSE_UNLIKELY(i > 16)) { + seed = mix(r8(p) ^ secret[1], r8(p + 8) ^ seed); + i -= 16; + p += 16; + } + a = r8(p + i - 16); + b = r8(p + i - 8); + } + + return mix(secret[1] ^ len, mix(a ^ secret[1], b ^ seed)); +} + +[[nodiscard]] static inline auto hash(uint64_t x) -> uint64_t { + return detail::wyhash::mix(x, UINT64_C(0x9E3779B97F4A7C15)); +} + +} // namespace detail::wyhash + +template <typename T, typename Enable = void> +struct hash { + auto operator()(T const& obj) const noexcept(noexcept(std::declval<std::hash<T>>().operator()(std::declval<T const&>()))) + -> uint64_t { + return std::hash<T>{}(obj); + } +}; + +template <typename CharT> +struct hash<std::basic_string<CharT>> { + using is_avalanching = void; + auto operator()(std::basic_string<CharT> const& str) const noexcept -> uint64_t { + return detail::wyhash::hash(str.data(), sizeof(CharT) * str.size()); + } +}; + +template <typename CharT> +struct hash<std::basic_string_view<CharT>> { + using is_avalanching = void; + auto operator()(std::basic_string_view<CharT> const& sv) const noexcept -> uint64_t { + return detail::wyhash::hash(sv.data(), sizeof(CharT) * sv.size()); + } +}; + +template <class T> +struct hash<T*> { + using is_avalanching = void; + auto operator()(T* ptr) const noexcept -> uint64_t { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + return detail::wyhash::hash(reinterpret_cast<uintptr_t>(ptr)); + } +}; + +template <class T> +struct hash<std::unique_ptr<T>> { + using is_avalanching = void; + auto operator()(std::unique_ptr<T> const& ptr) const noexcept -> uint64_t { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + return detail::wyhash::hash(reinterpret_cast<uintptr_t>(ptr.get())); + } +}; + +template <class T> +struct hash<std::shared_ptr<T>> { + using is_avalanching = void; + auto operator()(std::shared_ptr<T> const& ptr) const noexcept -> uint64_t { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + return detail::wyhash::hash(reinterpret_cast<uintptr_t>(ptr.get())); + } +}; + +template <typename Enum> +struct hash<Enum, typename std::enable_if<std::is_enum<Enum>::value>::type> { + using is_avalanching = void; + auto operator()(Enum e) const noexcept -> uint64_t { + using underlying = typename std::underlying_type_t<Enum>; + return detail::wyhash::hash(static_cast<underlying>(e)); + } +}; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +# define ANKERL_UNORDERED_DENSE_HASH_STATICCAST(T) \ + template <> \ + struct hash<T> { \ + using is_avalanching = void; \ + auto operator()(T const& obj) const noexcept -> uint64_t { \ + return detail::wyhash::hash(static_cast<uint64_t>(obj)); \ + } \ + } + +# if defined(__GNUC__) && !defined(__clang__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wuseless-cast" +# endif +// see https://en.cppreference.com/w/cpp/utility/hash +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(bool); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(char); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(signed char); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(unsigned char); +# if ANKERL_UNORDERED_DENSE_CPP_VERSION >= 202002L +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(char8_t); +# endif +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(char16_t); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(char32_t); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(wchar_t); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(short); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(unsigned short); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(int); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(unsigned int); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(long); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(long long); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(unsigned long); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(unsigned long long); + +# if defined(__GNUC__) && !defined(__clang__) +# pragma GCC diagnostic pop +# endif + +// bucket_type ////////////////////////////////////////////////////////// + +namespace bucket_type { + +struct standard { + static constexpr uint32_t dist_inc = 1U << 8U; // skip 1 byte fingerprint + static constexpr uint32_t fingerprint_mask = dist_inc - 1; // mask for 1 byte of fingerprint + + uint32_t m_dist_and_fingerprint; // upper 3 byte: distance to original bucket. lower byte: fingerprint from hash + uint32_t m_value_idx; // index into the m_values vector. +}; + +ANKERL_UNORDERED_DENSE_PACK(struct big { + static constexpr uint32_t dist_inc = 1U << 8U; // skip 1 byte fingerprint + static constexpr uint32_t fingerprint_mask = dist_inc - 1; // mask for 1 byte of fingerprint + + uint32_t m_dist_and_fingerprint; // upper 3 byte: distance to original bucket. lower byte: fingerprint from hash + size_t m_value_idx; // index into the m_values vector. +}); + +} // namespace bucket_type + +namespace detail { + +struct nonesuch {}; + +template <class Default, class AlwaysVoid, template <class...> class Op, class... Args> +struct detector { + using value_t = std::false_type; + using type = Default; +}; + +template <class Default, template <class...> class Op, class... Args> +struct detector<Default, std::void_t<Op<Args...>>, Op, Args...> { + using value_t = std::true_type; + using type = Op<Args...>; +}; + +template <template <class...> class Op, class... Args> +using is_detected = typename detail::detector<detail::nonesuch, void, Op, Args...>::value_t; + +template <template <class...> class Op, class... Args> +constexpr bool is_detected_v = is_detected<Op, Args...>::value; + +template <typename T> +using detect_avalanching = typename T::is_avalanching; + +template <typename T> +using detect_is_transparent = typename T::is_transparent; + +template <typename T> +using detect_iterator = typename T::iterator; + +// enable_if helpers + +template <typename Mapped> +constexpr bool is_map_v = !std::is_void_v<Mapped>; + +template <typename Hash, typename KeyEqual> +constexpr bool is_transparent_v = is_detected_v<detect_is_transparent, Hash>&& is_detected_v<detect_is_transparent, KeyEqual>; + +template <typename From, typename To1, typename To2> +constexpr bool is_neither_convertible_v = !std::is_convertible_v<From, To1> && !std::is_convertible_v<From, To2>; + +// This is it, the table. Doubles as map and set, and uses `void` for T when its used as a set. +template <class Key, + class T, // when void, treat it as a set. + class Hash, + class KeyEqual, + class AllocatorOrContainer, + class Bucket> +class table { +public: + using value_container_type = std::conditional_t< + is_detected_v<detect_iterator, AllocatorOrContainer>, + AllocatorOrContainer, + typename std::vector<typename std::conditional_t<std::is_void_v<T>, Key, std::pair<Key, T>>, AllocatorOrContainer>>; + +private: + using bucket_alloc = + typename std::allocator_traits<typename value_container_type::allocator_type>::template rebind_alloc<Bucket>; + using bucket_alloc_traits = std::allocator_traits<bucket_alloc>; + + static constexpr uint8_t initial_shifts = 64 - 3; // 2^(64-m_shift) number of buckets + static constexpr float default_max_load_factor = 0.8F; + +public: + using key_type = Key; + using mapped_type = T; + using value_type = typename value_container_type::value_type; + using size_type = typename value_container_type::size_type; + using difference_type = typename value_container_type::difference_type; + using hasher = Hash; + using key_equal = KeyEqual; + using allocator_type = typename value_container_type::allocator_type; + using reference = typename value_container_type::reference; + using const_reference = typename value_container_type::const_reference; + using pointer = typename value_container_type::pointer; + using const_pointer = typename value_container_type::const_pointer; + using iterator = typename value_container_type::iterator; + using const_iterator = typename value_container_type::const_iterator; + using bucket_type = Bucket; + +private: + using value_idx_type = decltype(Bucket::m_value_idx); + using dist_and_fingerprint_type = decltype(Bucket::m_dist_and_fingerprint); + + static_assert(std::is_trivially_destructible_v<Bucket>, "assert there's no need to call destructor / std::destroy"); + static_assert(std::is_trivially_copyable_v<Bucket>, "assert we can just memset / memcpy"); + + value_container_type m_values{}; // Contains all the key-value pairs in one densely stored container. No holes. + typename std::allocator_traits<bucket_alloc>::pointer m_buckets{}; + size_t m_num_buckets = 0; + size_t m_max_bucket_capacity = 0; + float m_max_load_factor = default_max_load_factor; + Hash m_hash{}; + KeyEqual m_equal{}; + uint8_t m_shifts = initial_shifts; + + [[nodiscard]] auto next(value_idx_type bucket_idx) const -> value_idx_type { + return ANKERL_UNORDERED_DENSE_UNLIKELY(bucket_idx + 1U == m_num_buckets) + ? 0 + : static_cast<value_idx_type>(bucket_idx + 1U); + } + + // Helper to access bucket through pointer types + [[nodiscard]] static constexpr auto at(typename std::allocator_traits<bucket_alloc>::pointer bucket_ptr, size_t offset) + -> Bucket& { + return *(bucket_ptr + static_cast<typename std::allocator_traits<bucket_alloc>::difference_type>(offset)); + } + + // use the dist_inc and dist_dec functions so that uint16_t types work without warning + [[nodiscard]] static constexpr auto dist_inc(dist_and_fingerprint_type x) -> dist_and_fingerprint_type { + return static_cast<dist_and_fingerprint_type>(x + Bucket::dist_inc); + } + + [[nodiscard]] static constexpr auto dist_dec(dist_and_fingerprint_type x) -> dist_and_fingerprint_type { + return static_cast<dist_and_fingerprint_type>(x - Bucket::dist_inc); + } + + // The goal of mixed_hash is to always produce a high quality 64bit hash. + template <typename K> + [[nodiscard]] constexpr auto mixed_hash(K const& key) const -> uint64_t { + if constexpr (is_detected_v<detect_avalanching, Hash>) { + // we know that the hash is good because is_avalanching. + if constexpr (sizeof(decltype(m_hash(key))) < sizeof(uint64_t)) { + // 32bit hash and is_avalanching => multiply with a constant to avalanche bits upwards + return m_hash(key) * UINT64_C(0x9ddfea08eb382d69); + } else { + // 64bit and is_avalanching => only use the hash itself. + return m_hash(key); + } + } else { + // not is_avalanching => apply wyhash + return wyhash::hash(m_hash(key)); + } + } + + [[nodiscard]] constexpr auto dist_and_fingerprint_from_hash(uint64_t hash) const -> dist_and_fingerprint_type { + return Bucket::dist_inc | (static_cast<dist_and_fingerprint_type>(hash) & Bucket::fingerprint_mask); + } + + [[nodiscard]] constexpr auto bucket_idx_from_hash(uint64_t hash) const -> value_idx_type { + return static_cast<value_idx_type>(hash >> m_shifts); + } + + [[nodiscard]] static constexpr auto get_key(value_type const& vt) -> key_type const& { + if constexpr (std::is_void_v<T>) { + return vt; + } else { + return vt.first; + } + } + + template <typename K> + [[nodiscard]] auto next_while_less(K const& key) const -> Bucket { + auto hash = mixed_hash(key); + auto dist_and_fingerprint = dist_and_fingerprint_from_hash(hash); + auto bucket_idx = bucket_idx_from_hash(hash); + + while (dist_and_fingerprint < at(m_buckets, bucket_idx).m_dist_and_fingerprint) { + dist_and_fingerprint = dist_inc(dist_and_fingerprint); + bucket_idx = next(bucket_idx); + } + return {dist_and_fingerprint, bucket_idx}; + } + + void place_and_shift_up(Bucket bucket, value_idx_type place) { + while (0 != at(m_buckets, place).m_dist_and_fingerprint) { + bucket = std::exchange(at(m_buckets, place), bucket); + bucket.m_dist_and_fingerprint = dist_inc(bucket.m_dist_and_fingerprint); + place = next(place); + } + at(m_buckets, place) = bucket; + } + + [[nodiscard]] static constexpr auto calc_num_buckets(uint8_t shifts) -> size_t { + return std::min(max_bucket_count(), size_t{1} << (64U - shifts)); + } + + [[nodiscard]] constexpr auto calc_shifts_for_size(size_t s) const -> uint8_t { + auto shifts = initial_shifts; + while (shifts > 0 && static_cast<size_t>(static_cast<float>(calc_num_buckets(shifts)) * max_load_factor()) < s) { + --shifts; + } + return shifts; + } + + // assumes m_values has data, m_buckets=m_buckets_end=nullptr, m_shifts is INITIAL_SHIFTS + void copy_buckets(table const& other) { + if (!empty()) { + m_shifts = other.m_shifts; + allocate_buckets_from_shift(); + std::memcpy(m_buckets, other.m_buckets, sizeof(Bucket) * bucket_count()); + } + } + + /** + * True when no element can be added any more without increasing the size + */ + [[nodiscard]] auto is_full() const -> bool { + return size() >= m_max_bucket_capacity; + } + + void deallocate_buckets() { + auto ba = bucket_alloc(m_values.get_allocator()); + if (nullptr != m_buckets) { + bucket_alloc_traits::deallocate(ba, m_buckets, bucket_count()); + } + m_buckets = nullptr; + m_num_buckets = 0; + m_max_bucket_capacity = 0; + } + + void allocate_buckets_from_shift() { + auto ba = bucket_alloc(m_values.get_allocator()); + m_num_buckets = calc_num_buckets(m_shifts); + m_buckets = bucket_alloc_traits::allocate(ba, m_num_buckets); + if (m_num_buckets == max_bucket_count()) { + // reached the maximum, make sure we can use each bucket + m_max_bucket_capacity = max_bucket_count(); + } else { + m_max_bucket_capacity = static_cast<value_idx_type>(static_cast<float>(m_num_buckets) * max_load_factor()); + } + } + + void clear_buckets() { + if (m_buckets != nullptr) { + std::memset(&*m_buckets, 0, sizeof(Bucket) * bucket_count()); + } + } + + void clear_and_fill_buckets_from_values() { + clear_buckets(); + for (value_idx_type value_idx = 0, end_idx = static_cast<value_idx_type>(m_values.size()); value_idx < end_idx; + ++value_idx) { + auto const& key = get_key(m_values[value_idx]); + auto [dist_and_fingerprint, bucket] = next_while_less(key); + + // we know for certain that key has not yet been inserted, so no need to check it. + place_and_shift_up({dist_and_fingerprint, value_idx}, bucket); + } + } + + void increase_size() { + if (ANKERL_UNORDERED_DENSE_UNLIKELY(m_max_bucket_capacity == max_bucket_count())) { + throw std::overflow_error("ankerl::unordered_dense: reached max bucket size, cannot increase size"); + } + --m_shifts; + deallocate_buckets(); + allocate_buckets_from_shift(); + clear_and_fill_buckets_from_values(); + } + + void do_erase(value_idx_type bucket_idx) { + auto const value_idx_to_remove = at(m_buckets, bucket_idx).m_value_idx; + + // shift down until either empty or an element with correct spot is found + auto next_bucket_idx = next(bucket_idx); + while (at(m_buckets, next_bucket_idx).m_dist_and_fingerprint >= Bucket::dist_inc * 2) { + at(m_buckets, bucket_idx) = {dist_dec(at(m_buckets, next_bucket_idx).m_dist_and_fingerprint), + at(m_buckets, next_bucket_idx).m_value_idx}; + bucket_idx = std::exchange(next_bucket_idx, next(next_bucket_idx)); + } + at(m_buckets, bucket_idx) = {}; + + // update m_values + if (value_idx_to_remove != m_values.size() - 1) { + // no luck, we'll have to replace the value with the last one and update the index accordingly + auto& val = m_values[value_idx_to_remove]; + val = std::move(m_values.back()); + + // update the values_idx of the moved entry. No need to play the info game, just look until we find the values_idx + auto mh = mixed_hash(get_key(val)); + bucket_idx = bucket_idx_from_hash(mh); + + auto const values_idx_back = static_cast<value_idx_type>(m_values.size() - 1); + while (values_idx_back != at(m_buckets, bucket_idx).m_value_idx) { + bucket_idx = next(bucket_idx); + } + at(m_buckets, bucket_idx).m_value_idx = value_idx_to_remove; + } + m_values.pop_back(); + } + + template <typename K> + auto do_erase_key(K&& key) -> size_t { + if (empty()) { + return 0; + } + + auto [dist_and_fingerprint, bucket_idx] = next_while_less(key); + + while (dist_and_fingerprint == at(m_buckets, bucket_idx).m_dist_and_fingerprint && + !m_equal(key, get_key(m_values[at(m_buckets, bucket_idx).m_value_idx]))) { + dist_and_fingerprint = dist_inc(dist_and_fingerprint); + bucket_idx = next(bucket_idx); + } + + if (dist_and_fingerprint != at(m_buckets, bucket_idx).m_dist_and_fingerprint) { + return 0; + } + do_erase(bucket_idx); + return 1; + } + + template <class K, class M> + auto do_insert_or_assign(K&& key, M&& mapped) -> std::pair<iterator, bool> { + auto it_isinserted = try_emplace(std::forward<K>(key), std::forward<M>(mapped)); + if (!it_isinserted.second) { + it_isinserted.first->second = std::forward<M>(mapped); + } + return it_isinserted; + } + + template <typename K, typename... Args> + auto do_place_element(dist_and_fingerprint_type dist_and_fingerprint, value_idx_type bucket_idx, K&& key, Args&&... args) + -> std::pair<iterator, bool> { + + // emplace the new value. If that throws an exception, no harm done; index is still in a valid state + m_values.emplace_back(std::piecewise_construct, + std::forward_as_tuple(std::forward<K>(key)), + std::forward_as_tuple(std::forward<Args>(args)...)); + + // place element and shift up until we find an empty spot + auto value_idx = static_cast<value_idx_type>(m_values.size() - 1); + place_and_shift_up({dist_and_fingerprint, value_idx}, bucket_idx); + return {begin() + static_cast<difference_type>(value_idx), true}; + } + + template <typename K, typename... Args> + auto do_try_emplace(K&& key, Args&&... args) -> std::pair<iterator, bool> { + if (ANKERL_UNORDERED_DENSE_UNLIKELY(is_full())) { + increase_size(); + } + + auto hash = mixed_hash(key); + auto dist_and_fingerprint = dist_and_fingerprint_from_hash(hash); + auto bucket_idx = bucket_idx_from_hash(hash); + + while (true) { + auto* bucket = &at(m_buckets, bucket_idx); + if (dist_and_fingerprint == bucket->m_dist_and_fingerprint) { + if (m_equal(key, m_values[bucket->m_value_idx].first)) { + return {begin() + static_cast<difference_type>(bucket->m_value_idx), false}; + } + } else if (dist_and_fingerprint > bucket->m_dist_and_fingerprint) { + return do_place_element(dist_and_fingerprint, bucket_idx, std::forward<K>(key), std::forward<Args>(args)...); + } + dist_and_fingerprint = dist_inc(dist_and_fingerprint); + bucket_idx = next(bucket_idx); + } + } + + template <typename K> + auto do_find(K const& key) -> iterator { + if (ANKERL_UNORDERED_DENSE_UNLIKELY(empty())) { + return end(); + } + + auto mh = mixed_hash(key); + auto dist_and_fingerprint = dist_and_fingerprint_from_hash(mh); + auto bucket_idx = bucket_idx_from_hash(mh); + auto* bucket = &at(m_buckets, bucket_idx); + + // unrolled loop. *Always* check a few directly, then enter the loop. This is faster. + if (dist_and_fingerprint == bucket->m_dist_and_fingerprint && m_equal(key, get_key(m_values[bucket->m_value_idx]))) { + return begin() + static_cast<difference_type>(bucket->m_value_idx); + } + dist_and_fingerprint = dist_inc(dist_and_fingerprint); + bucket_idx = next(bucket_idx); + bucket = &at(m_buckets, bucket_idx); + + if (dist_and_fingerprint == bucket->m_dist_and_fingerprint && m_equal(key, get_key(m_values[bucket->m_value_idx]))) { + return begin() + static_cast<difference_type>(bucket->m_value_idx); + } + dist_and_fingerprint = dist_inc(dist_and_fingerprint); + bucket_idx = next(bucket_idx); + bucket = &at(m_buckets, bucket_idx); + + while (true) { + if (dist_and_fingerprint == bucket->m_dist_and_fingerprint) { + if (m_equal(key, get_key(m_values[bucket->m_value_idx]))) { + return begin() + static_cast<difference_type>(bucket->m_value_idx); + } + } else if (dist_and_fingerprint > bucket->m_dist_and_fingerprint) { + return end(); + } + dist_and_fingerprint = dist_inc(dist_and_fingerprint); + bucket_idx = next(bucket_idx); + bucket = &at(m_buckets, bucket_idx); + } + } + + template <typename K> + auto do_find(K const& key) const -> const_iterator { + return const_cast<table*>(this)->do_find(key); // NOLINT(cppcoreguidelines-pro-type-const-cast) + } + + template <typename K, typename Q = T, std::enable_if_t<is_map_v<Q>, bool> = true> + auto do_at(K const& key) -> Q& { + if (auto it = find(key); end() != it) { + return it->second; + } + throw std::out_of_range("ankerl::unordered_dense::map::at(): key not found"); + } + + template <typename K, typename Q = T, std::enable_if_t<is_map_v<Q>, bool> = true> + auto do_at(K const& key) const -> Q const& { + return const_cast<table*>(this)->at(key); // NOLINT(cppcoreguidelines-pro-type-const-cast) + } + +public: + table() + : table(0) {} + + explicit table(size_t /*bucket_count*/, + Hash const& hash = Hash(), + KeyEqual const& equal = KeyEqual(), + allocator_type const& alloc_or_container = allocator_type()) + : m_values(alloc_or_container) + , m_hash(hash) + , m_equal(equal) {} + + table(size_t bucket_count, allocator_type const& alloc) + : table(bucket_count, Hash(), KeyEqual(), alloc) {} + + table(size_t bucket_count, Hash const& hash, allocator_type const& alloc) + : table(bucket_count, hash, KeyEqual(), alloc) {} + + explicit table(allocator_type const& alloc) + : table(0, Hash(), KeyEqual(), alloc) {} + + template <class InputIt> + table(InputIt first, + InputIt last, + size_type bucket_count = 0, + Hash const& hash = Hash(), + KeyEqual const& equal = KeyEqual(), + allocator_type const& alloc = allocator_type()) + : table(bucket_count, hash, equal, alloc) { + insert(first, last); + } + + template <class InputIt> + table(InputIt first, InputIt last, size_type bucket_count, allocator_type const& alloc) + : table(first, last, bucket_count, Hash(), KeyEqual(), alloc) {} + + template <class InputIt> + table(InputIt first, InputIt last, size_type bucket_count, Hash const& hash, allocator_type const& alloc) + : table(first, last, bucket_count, hash, KeyEqual(), alloc) {} + + table(table const& other) + : table(other, other.m_values.get_allocator()) {} + + table(table const& other, allocator_type const& alloc) + : m_values(other.m_values, alloc) + , m_max_load_factor(other.m_max_load_factor) + , m_hash(other.m_hash) + , m_equal(other.m_equal) { + copy_buckets(other); + } + + table(table&& other) noexcept + : table(std::move(other), other.m_values.get_allocator()) {} + + table(table&& other, allocator_type const& alloc) noexcept + : m_values(std::move(other.m_values), alloc) + , m_buckets(std::exchange(other.m_buckets, nullptr)) + , m_num_buckets(std::exchange(other.m_num_buckets, 0)) + , m_max_bucket_capacity(std::exchange(other.m_max_bucket_capacity, 0)) + , m_max_load_factor(std::exchange(other.m_max_load_factor, default_max_load_factor)) + , m_hash(std::exchange(other.m_hash, {})) + , m_equal(std::exchange(other.m_equal, {})) + , m_shifts(std::exchange(other.m_shifts, initial_shifts)) { + other.m_values.clear(); + } + + table(std::initializer_list<value_type> ilist, + size_t bucket_count = 0, + Hash const& hash = Hash(), + KeyEqual const& equal = KeyEqual(), + allocator_type const& alloc = allocator_type()) + : table(bucket_count, hash, equal, alloc) { + insert(ilist); + } + + table(std::initializer_list<value_type> ilist, size_type bucket_count, allocator_type const& alloc) + : table(ilist, bucket_count, Hash(), KeyEqual(), alloc) {} + + table(std::initializer_list<value_type> init, size_type bucket_count, Hash const& hash, allocator_type const& alloc) + : table(init, bucket_count, hash, KeyEqual(), alloc) {} + + ~table() { + auto ba = bucket_alloc(m_values.get_allocator()); + bucket_alloc_traits::deallocate(ba, m_buckets, bucket_count()); + } + + auto operator=(table const& other) -> table& { + if (&other != this) { + deallocate_buckets(); // deallocate before m_values is set (might have another allocator) + m_values = other.m_values; + m_max_load_factor = other.m_max_load_factor; + m_hash = other.m_hash; + m_equal = other.m_equal; + m_shifts = initial_shifts; + copy_buckets(other); + } + return *this; + } + + auto operator=(table&& other) noexcept( + noexcept(std::is_nothrow_move_assignable_v<value_container_type>&& std::is_nothrow_move_assignable_v<Hash>&& + std::is_nothrow_move_assignable_v<KeyEqual>)) -> table& { + if (&other != this) { + deallocate_buckets(); // deallocate before m_values is set (might have another allocator) + m_values = std::move(other.m_values); + m_buckets = std::exchange(other.m_buckets, nullptr); + m_num_buckets = std::exchange(other.m_num_buckets, 0); + m_max_bucket_capacity = std::exchange(other.m_max_bucket_capacity, 0); + m_max_load_factor = std::exchange(other.m_max_load_factor, default_max_load_factor); + m_hash = std::exchange(other.m_hash, {}); + m_equal = std::exchange(other.m_equal, {}); + m_shifts = std::exchange(other.m_shifts, initial_shifts); + other.m_values.clear(); + } + return *this; + } + + auto operator=(std::initializer_list<value_type> ilist) -> table& { + clear(); + insert(ilist); + return *this; + } + + auto get_allocator() const noexcept -> allocator_type { + return m_values.get_allocator(); + } + + // iterators ////////////////////////////////////////////////////////////// + + auto begin() noexcept -> iterator { + return m_values.begin(); + } + + auto begin() const noexcept -> const_iterator { + return m_values.begin(); + } + + auto cbegin() const noexcept -> const_iterator { + return m_values.cbegin(); + } + + auto end() noexcept -> iterator { + return m_values.end(); + } + + auto cend() const noexcept -> const_iterator { + return m_values.cend(); + } + + auto end() const noexcept -> const_iterator { + return m_values.end(); + } + + // capacity /////////////////////////////////////////////////////////////// + + [[nodiscard]] auto empty() const noexcept -> bool { + return m_values.empty(); + } + + [[nodiscard]] auto size() const noexcept -> size_t { + return m_values.size(); + } + + [[nodiscard]] static constexpr auto max_size() noexcept -> size_t { + if constexpr (std::numeric_limits<value_idx_type>::max() == std::numeric_limits<size_t>::max()) { + return size_t{1} << (sizeof(value_idx_type) * 8 - 1); + } else { + return size_t{1} << (sizeof(value_idx_type) * 8); + } + } + + // modifiers ////////////////////////////////////////////////////////////// + + void clear() { + m_values.clear(); + clear_buckets(); + } + + auto insert(value_type const& value) -> std::pair<iterator, bool> { + return emplace(value); + } + + auto insert(value_type&& value) -> std::pair<iterator, bool> { + return emplace(std::move(value)); + } + + template <class P, std::enable_if_t<std::is_constructible_v<value_type, P&&>, bool> = true> + auto insert(P&& value) -> std::pair<iterator, bool> { + return emplace(std::forward<P>(value)); + } + + auto insert(const_iterator /*hint*/, value_type const& value) -> iterator { + return insert(value).first; + } + + auto insert(const_iterator /*hint*/, value_type&& value) -> iterator { + return insert(std::move(value)).first; + } + + template <class P, std::enable_if_t<std::is_constructible_v<value_type, P&&>, bool> = true> + auto insert(const_iterator /*hint*/, P&& value) -> iterator { + return insert(std::forward<P>(value)).first; + } + + template <class InputIt> + void insert(InputIt first, InputIt last) { + while (first != last) { + insert(*first); + ++first; + } + } + + void insert(std::initializer_list<value_type> ilist) { + insert(ilist.begin(), ilist.end()); + } + + // nonstandard API: *this is emptied. + // Also see "A Standard flat_map" https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p0429r9.pdf + auto extract() && -> value_container_type { + return std::move(m_values); + } + + // nonstandard API: + // Discards the internally held container and replaces it with the one passed. Erases non-unique elements. + auto replace(value_container_type&& container) { + if (container.size() > max_size()) { + throw std::out_of_range("ankerl::unordered_dense::map::replace(): too many elements"); + } + + auto shifts = calc_shifts_for_size(container.size()); + if (0 == m_num_buckets || shifts < m_shifts || container.get_allocator() != m_values.get_allocator()) { + m_shifts = shifts; + deallocate_buckets(); + allocate_buckets_from_shift(); + } + clear_buckets(); + + m_values = std::move(container); + + // can't use clear_and_fill_buckets_from_values() because container elements might not be unique + auto value_idx = value_idx_type{}; + + // loop until we reach the end of the container. duplicated entries will be replaced with back(). + while (value_idx != static_cast<value_idx_type>(m_values.size())) { + auto const& key = get_key(m_values[value_idx]); + + auto hash = mixed_hash(key); + auto dist_and_fingerprint = dist_and_fingerprint_from_hash(hash); + auto bucket_idx = bucket_idx_from_hash(hash); + + bool key_found = false; + while (true) { + auto const& bucket = at(m_buckets, bucket_idx); + if (dist_and_fingerprint > bucket.m_dist_and_fingerprint) { + break; + } + if (dist_and_fingerprint == bucket.m_dist_and_fingerprint && + m_equal(key, m_values[bucket.m_value_idx].first)) { + key_found = true; + break; + } + dist_and_fingerprint = dist_inc(dist_and_fingerprint); + bucket_idx = next(bucket_idx); + } + + if (key_found) { + if (value_idx != static_cast<value_idx_type>(m_values.size() - 1)) { + m_values[value_idx] = std::move(m_values.back()); + } + m_values.pop_back(); + } else { + place_and_shift_up({dist_and_fingerprint, value_idx}, bucket_idx); + ++value_idx; + } + } + } + + template <class M, typename Q = T, std::enable_if_t<is_map_v<Q>, bool> = true> + auto insert_or_assign(Key const& key, M&& mapped) -> std::pair<iterator, bool> { + return do_insert_or_assign(key, std::forward<M>(mapped)); + } + + template <class M, typename Q = T, std::enable_if_t<is_map_v<Q>, bool> = true> + auto insert_or_assign(Key&& key, M&& mapped) -> std::pair<iterator, bool> { + return do_insert_or_assign(std::move(key), std::forward<M>(mapped)); + } + + template <typename K, + typename M, + typename Q = T, + typename H = Hash, + typename KE = KeyEqual, + std::enable_if_t<is_map_v<Q> && is_transparent_v<H, KE>, bool> = true> + auto insert_or_assign(K&& key, M&& mapped) -> std::pair<iterator, bool> { + return do_insert_or_assign(std::forward<K>(key), std::forward<M>(mapped)); + } + + template <class M, typename Q = T, std::enable_if_t<is_map_v<Q>, bool> = true> + auto insert_or_assign(const_iterator /*hint*/, Key const& key, M&& mapped) -> iterator { + return do_insert_or_assign(key, std::forward<M>(mapped)).first; + } + + template <class M, typename Q = T, std::enable_if_t<is_map_v<Q>, bool> = true> + auto insert_or_assign(const_iterator /*hint*/, Key&& key, M&& mapped) -> iterator { + return do_insert_or_assign(std::move(key), std::forward<M>(mapped)).first; + } + + template <typename K, + typename M, + typename Q = T, + typename H = Hash, + typename KE = KeyEqual, + std::enable_if_t<is_map_v<Q> && is_transparent_v<H, KE>, bool> = true> + auto insert_or_assign(const_iterator /*hint*/, K&& key, M&& mapped) -> iterator { + return do_insert_or_assign(std::forward<K>(key), std::forward<M>(mapped)).first; + } + + // Single arguments for unordered_set can be used without having to construct the value_type + template <class K, + typename Q = T, + typename H = Hash, + typename KE = KeyEqual, + std::enable_if_t<!is_map_v<Q> && is_transparent_v<H, KE>, bool> = true> + auto emplace(K&& key) -> std::pair<iterator, bool> { + if (is_full()) { + increase_size(); + } + + auto hash = mixed_hash(key); + auto dist_and_fingerprint = dist_and_fingerprint_from_hash(hash); + auto bucket_idx = bucket_idx_from_hash(hash); + + while (dist_and_fingerprint <= at(m_buckets, bucket_idx).m_dist_and_fingerprint) { + if (dist_and_fingerprint == at(m_buckets, bucket_idx).m_dist_and_fingerprint && + m_equal(key, m_values[at(m_buckets, bucket_idx).m_value_idx])) { + // found it, return without ever actually creating anything + return {begin() + static_cast<difference_type>(at(m_buckets, bucket_idx).m_value_idx), false}; + } + dist_and_fingerprint = dist_inc(dist_and_fingerprint); + bucket_idx = next(bucket_idx); + } + + // value is new, insert element first, so when exception happens we are in a valid state + m_values.emplace_back(std::forward<K>(key)); + // now place the bucket and shift up until we find an empty spot + auto value_idx = static_cast<value_idx_type>(m_values.size() - 1); + place_and_shift_up({dist_and_fingerprint, value_idx}, bucket_idx); + return {begin() + static_cast<difference_type>(value_idx), true}; + } + + template <class... Args> + auto emplace(Args&&... args) -> std::pair<iterator, bool> { + if (is_full()) { + increase_size(); + } + + // we have to instantiate the value_type to be able to access the key. + // 1. emplace_back the object so it is constructed. 2. If the key is already there, pop it later in the loop. + auto& key = get_key(m_values.emplace_back(std::forward<Args>(args)...)); + auto hash = mixed_hash(key); + auto dist_and_fingerprint = dist_and_fingerprint_from_hash(hash); + auto bucket_idx = bucket_idx_from_hash(hash); + + while (dist_and_fingerprint <= at(m_buckets, bucket_idx).m_dist_and_fingerprint) { + if (dist_and_fingerprint == at(m_buckets, bucket_idx).m_dist_and_fingerprint && + m_equal(key, get_key(m_values[at(m_buckets, bucket_idx).m_value_idx]))) { + m_values.pop_back(); // value was already there, so get rid of it + return {begin() + static_cast<difference_type>(at(m_buckets, bucket_idx).m_value_idx), false}; + } + dist_and_fingerprint = dist_inc(dist_and_fingerprint); + bucket_idx = next(bucket_idx); + } + + // value is new, place the bucket and shift up until we find an empty spot + auto value_idx = static_cast<value_idx_type>(m_values.size() - 1); + place_and_shift_up({dist_and_fingerprint, value_idx}, bucket_idx); + + return {begin() + static_cast<difference_type>(value_idx), true}; + } + + template <class... Args> + auto emplace_hint(const_iterator /*hint*/, Args&&... args) -> iterator { + return emplace(std::forward<Args>(args)...).first; + } + + template <class... Args, typename Q = T, std::enable_if_t<is_map_v<Q>, bool> = true> + auto try_emplace(Key const& key, Args&&... args) -> std::pair<iterator, bool> { + return do_try_emplace(key, std::forward<Args>(args)...); + } + + template <class... Args, typename Q = T, std::enable_if_t<is_map_v<Q>, bool> = true> + auto try_emplace(Key&& key, Args&&... args) -> std::pair<iterator, bool> { + return do_try_emplace(std::move(key), std::forward<Args>(args)...); + } + + template <class... Args, typename Q = T, std::enable_if_t<is_map_v<Q>, bool> = true> + auto try_emplace(const_iterator /*hint*/, Key const& key, Args&&... args) -> iterator { + return do_try_emplace(key, std::forward<Args>(args)...).first; + } + + template <class... Args, typename Q = T, std::enable_if_t<is_map_v<Q>, bool> = true> + auto try_emplace(const_iterator /*hint*/, Key&& key, Args&&... args) -> iterator { + return do_try_emplace(std::move(key), std::forward<Args>(args)...).first; + } + + template < + typename K, + typename... Args, + typename Q = T, + typename H = Hash, + typename KE = KeyEqual, + std::enable_if_t<is_map_v<Q> && is_transparent_v<H, KE> && is_neither_convertible_v<K&&, iterator, const_iterator>, + bool> = true> + auto try_emplace(K&& key, Args&&... args) -> std::pair<iterator, bool> { + return do_try_emplace(std::forward<K>(key), std::forward<Args>(args)...); + } + + template < + typename K, + typename... Args, + typename Q = T, + typename H = Hash, + typename KE = KeyEqual, + std::enable_if_t<is_map_v<Q> && is_transparent_v<H, KE> && is_neither_convertible_v<K&&, iterator, const_iterator>, + bool> = true> + auto try_emplace(const_iterator /*hint*/, K&& key, Args&&... args) -> iterator { + return do_try_emplace(std::forward<K>(key), std::forward<Args>(args)...).first; + } + + auto erase(iterator it) -> iterator { + auto hash = mixed_hash(get_key(*it)); + auto bucket_idx = bucket_idx_from_hash(hash); + + auto const value_idx_to_remove = static_cast<value_idx_type>(it - cbegin()); + while (at(m_buckets, bucket_idx).m_value_idx != value_idx_to_remove) { + bucket_idx = next(bucket_idx); + } + + do_erase(bucket_idx); + return begin() + static_cast<difference_type>(value_idx_to_remove); + } + + auto erase(const_iterator it) -> iterator { + return erase(begin() + (it - cbegin())); + } + + auto erase(const_iterator first, const_iterator last) -> iterator { + auto const idx_first = first - cbegin(); + auto const idx_last = last - cbegin(); + auto const first_to_last = std::distance(first, last); + auto const last_to_end = std::distance(last, cend()); + + // remove elements from left to right which moves elements from the end back + auto const mid = idx_first + std::min(first_to_last, last_to_end); + auto idx = idx_first; + while (idx != mid) { + erase(begin() + idx); + ++idx; + } + + // all elements from the right are moved, now remove the last element until all done + idx = idx_last; + while (idx != mid) { + --idx; + erase(begin() + idx); + } + + return begin() + idx_first; + } + + auto erase(Key const& key) -> size_t { + return do_erase_key(key); + } + + template <class K, class H = Hash, class KE = KeyEqual, std::enable_if_t<is_transparent_v<H, KE>, bool> = true> + auto erase(K&& key) -> size_t { + return do_erase_key(std::forward<K>(key)); + } + + void swap(table& other) noexcept(noexcept(std::is_nothrow_swappable_v<value_container_type>&& + std::is_nothrow_swappable_v<Hash>&& std::is_nothrow_swappable_v<KeyEqual>)) { + using std::swap; + swap(other, *this); + } + + // lookup ///////////////////////////////////////////////////////////////// + + template <typename Q = T, std::enable_if_t<is_map_v<Q>, bool> = true> + auto at(key_type const& key) -> Q& { + return do_at(key); + } + + template <typename K, + typename Q = T, + typename H = Hash, + typename KE = KeyEqual, + std::enable_if_t<is_map_v<Q> && is_transparent_v<H, KE>, bool> = true> + auto at(K const& key) -> Q& { + return do_at(key); + } + + template <typename Q = T, std::enable_if_t<is_map_v<Q>, bool> = true> + auto at(key_type const& key) const -> Q const& { + return do_at(key); + } + + template <typename K, + typename Q = T, + typename H = Hash, + typename KE = KeyEqual, + std::enable_if_t<is_map_v<Q> && is_transparent_v<H, KE>, bool> = true> + auto at(K const& key) const -> Q const& { + return do_at(key); + } + + template <typename Q = T, std::enable_if_t<is_map_v<Q>, bool> = true> + auto operator[](Key const& key) -> Q& { + return try_emplace(key).first->second; + } + + template <typename Q = T, std::enable_if_t<is_map_v<Q>, bool> = true> + auto operator[](Key&& key) -> Q& { + return try_emplace(std::move(key)).first->second; + } + + template <typename K, + typename Q = T, + typename H = Hash, + typename KE = KeyEqual, + std::enable_if_t<is_map_v<Q> && is_transparent_v<H, KE>, bool> = true> + auto operator[](K&& key) -> Q& { + return try_emplace(std::forward<K>(key)).first->second; + } + + auto count(Key const& key) const -> size_t { + return find(key) == end() ? 0 : 1; + } + + template <class K, class H = Hash, class KE = KeyEqual, std::enable_if_t<is_transparent_v<H, KE>, bool> = true> + auto count(K const& key) const -> size_t { + return find(key) == end() ? 0 : 1; + } + + auto find(Key const& key) -> iterator { + return do_find(key); + } + + auto find(Key const& key) const -> const_iterator { + return do_find(key); + } + + template <class K, class H = Hash, class KE = KeyEqual, std::enable_if_t<is_transparent_v<H, KE>, bool> = true> + auto find(K const& key) -> iterator { + return do_find(key); + } + + template <class K, class H = Hash, class KE = KeyEqual, std::enable_if_t<is_transparent_v<H, KE>, bool> = true> + auto find(K const& key) const -> const_iterator { + return do_find(key); + } + + auto contains(Key const& key) const -> bool { + return find(key) != end(); + } + + template <class K, class H = Hash, class KE = KeyEqual, std::enable_if_t<is_transparent_v<H, KE>, bool> = true> + auto contains(K const& key) const -> bool { + return find(key) != end(); + } + + auto equal_range(Key const& key) -> std::pair<iterator, iterator> { + auto it = do_find(key); + return {it, it == end() ? end() : it + 1}; + } + + auto equal_range(const Key& key) const -> std::pair<const_iterator, const_iterator> { + auto it = do_find(key); + return {it, it == end() ? end() : it + 1}; + } + + template <class K, class H = Hash, class KE = KeyEqual, std::enable_if_t<is_transparent_v<H, KE>, bool> = true> + auto equal_range(K const& key) -> std::pair<iterator, iterator> { + auto it = do_find(key); + return {it, it == end() ? end() : it + 1}; + } + + template <class K, class H = Hash, class KE = KeyEqual, std::enable_if_t<is_transparent_v<H, KE>, bool> = true> + auto equal_range(K const& key) const -> std::pair<const_iterator, const_iterator> { + auto it = do_find(key); + return {it, it == end() ? end() : it + 1}; + } + + // bucket interface /////////////////////////////////////////////////////// + + auto bucket_count() const noexcept -> size_t { // NOLINT(modernize-use-nodiscard) + return m_num_buckets; + } + + static constexpr auto max_bucket_count() noexcept -> size_t { // NOLINT(modernize-use-nodiscard) + return max_size(); + } + + // hash policy //////////////////////////////////////////////////////////// + + [[nodiscard]] auto load_factor() const -> float { + return bucket_count() ? static_cast<float>(size()) / static_cast<float>(bucket_count()) : 0.0F; + } + + [[nodiscard]] auto max_load_factor() const -> float { + return m_max_load_factor; + } + + void max_load_factor(float ml) { + m_max_load_factor = ml; + if (m_num_buckets != max_bucket_count()) { + m_max_bucket_capacity = static_cast<value_idx_type>(static_cast<float>(bucket_count()) * max_load_factor()); + } + } + + void rehash(size_t count) { + count = std::min(count, max_size()); + auto shifts = calc_shifts_for_size(std::max(count, size())); + if (shifts != m_shifts) { + m_shifts = shifts; + deallocate_buckets(); + m_values.shrink_to_fit(); + allocate_buckets_from_shift(); + clear_and_fill_buckets_from_values(); + } + } + + void reserve(size_t capa) { + capa = std::min(capa, max_size()); + m_values.reserve(capa); + auto shifts = calc_shifts_for_size(std::max(capa, size())); + if (0 == m_num_buckets || shifts < m_shifts) { + m_shifts = shifts; + deallocate_buckets(); + allocate_buckets_from_shift(); + clear_and_fill_buckets_from_values(); + } + } + + // observers ////////////////////////////////////////////////////////////// + + auto hash_function() const -> hasher { + return m_hash; + } + + auto key_eq() const -> key_equal { + return m_equal; + } + + // nonstandard API: expose the underlying values container + [[nodiscard]] auto values() const noexcept -> value_container_type const& { + return m_values; + } + + // non-member functions /////////////////////////////////////////////////// + + friend auto operator==(table const& a, table const& b) -> bool { + if (&a == &b) { + return true; + } + if (a.size() != b.size()) { + return false; + } + for (auto const& b_entry : b) { + auto it = a.find(get_key(b_entry)); + if constexpr (std::is_void_v<T>) { + // set: only check that the key is here + if (a.end() == it) { + return false; + } + } else { + // map: check that key is here, then also check that value is the same + if (a.end() == it || !(b_entry.second == it->second)) { + return false; + } + } + } + return true; + } + + friend auto operator!=(table const& a, table const& b) -> bool { + return !(a == b); + } +}; + +} // namespace detail + +template <class Key, + class T, + class Hash = hash<Key>, + class KeyEqual = std::equal_to<Key>, + class AllocatorOrContainer = std::allocator<std::pair<Key, T>>, + class Bucket = bucket_type::standard> +using map = detail::table<Key, T, Hash, KeyEqual, AllocatorOrContainer, Bucket>; + +template <class Key, + class Hash = hash<Key>, + class KeyEqual = std::equal_to<Key>, + class AllocatorOrContainer = std::allocator<Key>, + class Bucket = bucket_type::standard> +using set = detail::table<Key, void, Hash, KeyEqual, AllocatorOrContainer, Bucket>; + +# if ANKERL_UNORDERED_DENSE_PMR + +namespace pmr { + +template <class Key, + class T, + class Hash = hash<Key>, + class KeyEqual = std::equal_to<Key>, + class Bucket = bucket_type::standard> +using map = detail::table<Key, T, Hash, KeyEqual, std::pmr::polymorphic_allocator<std::pair<Key, T>>, Bucket>; + +template <class Key, class Hash = hash<Key>, class KeyEqual = std::equal_to<Key>, class Bucket = bucket_type::standard> +using set = detail::table<Key, void, Hash, KeyEqual, std::pmr::polymorphic_allocator<Key>, Bucket>; + +} // namespace pmr + +# endif + +// deduction guides /////////////////////////////////////////////////////////// + +// deduction guides for alias templates are only possible since C++20 +// see https://en.cppreference.com/w/cpp/language/class_template_argument_deduction + +} // namespace ANKERL_UNORDERED_DENSE_NAMESPACE +} // namespace ankerl::unordered_dense + +// std extensions ///////////////////////////////////////////////////////////// + +namespace std { // NOLINT(cert-dcl58-cpp) + +template <class Key, class T, class Hash, class KeyEqual, class AllocatorOrContainer, class Bucket, class Pred> +auto erase_if(ankerl::unordered_dense::detail::table<Key, T, Hash, KeyEqual, AllocatorOrContainer, Bucket>& map, Pred pred) + -> size_t { + using map_t = ankerl::unordered_dense::detail::table<Key, T, Hash, KeyEqual, AllocatorOrContainer, Bucket>; + + // going back to front because erase() invalidates the end iterator + auto const old_size = map.size(); + auto idx = old_size; + while (idx) { + --idx; + auto it = map.begin() + static_cast<typename map_t::difference_type>(idx); + if (pred(*it)) { + map.erase(it); + } + } + + return map.size() - old_size; +} + +} // namespace std + +#endif +#endif diff --git a/misc/benchmarks/external/emhash/hash_table7.hpp b/misc/benchmarks/external/emhash/hash_table7.hpp new file mode 100644 index 00000000..fdc33fe1 --- /dev/null +++ b/misc/benchmarks/external/emhash/hash_table7.hpp @@ -0,0 +1,1876 @@ +// emhash7::HashMap for C++11/14/17 +// version 2.2.3 +// https://github.com/ktprime/ktprime/blob/master/hash_table7.hpp +// +// Licensed under the MIT License <http://opensource.org/licenses/MIT>. +// SPDX-License-Identifier: MIT +// Copyright (c) 2019-2022 Huang Yuanbing & bailuzhou AT 163.com +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE + +// From +// NUMBER OF PROBES / LOOKUP Successful Unsuccessful +// Quadratic collision resolution 1 - ln(1-L) - L/2 1/(1-L) - L - ln(1-L) +// Linear collision resolution [1+1/(1-L)]/2 [1+1/(1-L)2]/2 +// separator chain resolution 1 + L / 2 exp(-L) + L + +// -- enlarge_factor -- 0.10 0.50 0.60 0.75 0.80 0.90 0.99 +// QUADRATIC COLLISION RES. +// probes/successful lookup 1.05 1.44 1.62 2.01 2.21 2.85 5.11 +// probes/unsuccessful lookup 1.11 2.19 2.82 4.64 5.81 11.4 103.6 +// LINEAR COLLISION RES. +// probes/successful lookup 1.06 1.5 1.75 2.5 3.0 5.5 50.5 +// probes/unsuccessful lookup 1.12 2.5 3.6 8.5 13.0 50.0 +// SEPARATE CHAN RES. +// probes/successful lookup 1.05 1.25 1.3 1.25 1.4 1.45 1.50 +// probes/unsuccessful lookup 1.00 1.11 1.15 1.22 1.25 1.31 1.37 +// clacul/unsuccessful lookup 1.01 1.25 1.36, 1.56, 1.64, 1.81, 1.97 + +/**************** + under random hashCodes, the frequency of nodes in bins follows a Poisson +distribution(http://en.wikipedia.org/wiki/Poisson_distribution) with a parameter of about 0.5 +on average for the default resizing threshold of 0.75, although with a large variance because +of resizing granularity. Ignoring variance, the expected occurrences of list size k are +(exp(-0.5) * pow(0.5, k)/factorial(k)). The first values are: +0: 0.60653066 +1: 0.30326533 +2: 0.07581633 +3: 0.01263606 +4: 0.00157952 +5: 0.00015795 +6: 0.00001316 +7: 0.00000094 +8: 0.00000006 + + ============== buckets size ration ======== + 1 1543981 0.36884964|0.36787944 36.885 + 2 768655 0.36725597|0.36787944 73.611 + 3 256236 0.18364065|0.18393972 91.975 + 4 64126 0.06127757|0.06131324 98.102 + 5 12907 0.01541710|0.01532831 99.644 + 6 2050 0.00293841|0.00306566 99.938 + 7 310 0.00051840|0.00051094 99.990 + 8 49 0.00009365|0.00007299 99.999 + 9 4 0.00000860|0.00000913 100.000 +========== collision miss ration =========== + _num_filled aver_size k.v size_kv = 4185936, 1.58, x.x 24 + collision,possion,cache_miss hit_find|hit_miss, load_factor = 36.73%,36.74%,31.31% 1.50|2.00, 1.00 +============== buckets size ration ======== +*******************************************************/ + +#pragma once + +#include <cstring> +#include <string> +#include <cmath> +#include <cstdlib> +#include <type_traits> +#include <cassert> +#include <utility> +#include <cstdint> +#include <functional> +#include <iterator> +#include <algorithm> + +#if EMH_WY_HASH + #include "wyhash.h" +#endif + +#ifdef EMH_KEY + #undef EMH_KEY + #undef EMH_VAL + #undef EMH_PKV + #undef EMH_NEW + #undef EMH_SET + #undef EMH_BUCKET + #undef EMH_EMPTY +#endif + +// likely/unlikely +#if (__GNUC__ >= 4 || __clang__) +# define EMH_LIKELY(condition) __builtin_expect(condition, 1) +# define EMH_UNLIKELY(condition) __builtin_expect(condition, 0) +#else +# define EMH_LIKELY(condition) condition +# define EMH_UNLIKELY(condition) condition +#endif + +#ifndef EMH_BUCKET_INDEX + #define EMH_BUCKET_INDEX 1 +#endif + +#if EMH_BUCKET_INDEX == 0 + #define EMH_KEY(p,n) p[n].second.first + #define EMH_VAL(p,n) p[n].second.second + #define EMH_BUCKET(p,n) p[n].first + #define EMH_PKV(p,n) p[n].second + #define EMH_NEW(key, val, bucket)\ + new(_pairs + bucket) PairT(bucket, value_type(key, val));\ + _num_filled ++; EMH_SET(bucket) +#elif EMH_BUCKET_INDEX == 2 + #define EMH_KEY(p,n) p[n].first.first + #define EMH_VAL(p,n) p[n].first.second + #define EMH_BUCKET(p,n) p[n].second + #define EMH_PKV(p,n) p[n].first + #define EMH_NEW(key, val, bucket)\ + new(_pairs + bucket) PairT(value_type(key, val), bucket);\ + _num_filled ++; EMH_SET(bucket) +#else + #define EMH_KEY(p,n) p[n].first + #define EMH_VAL(p,n) p[n].second + #define EMH_BUCKET(p,n) p[n].bucket + #define EMH_PKV(p,n) p[n] + #define EMH_NEW(key, val, bucket)\ + new(_pairs + bucket) PairT(key, val, bucket);\ + _num_filled ++; EMH_SET(bucket) +#endif + +#define EMH_MASK(bucket) 1 << (bucket % MASK_BIT) +#define EMH_SET(bucket) _bitmask[bucket / MASK_BIT] &= ~(EMH_MASK(bucket)) +#define EMH_CLS(bucket) _bitmask[bucket / MASK_BIT] |= EMH_MASK(bucket) +#define EMH_EMPTY(bitmask, bucket) (_bitmask[bucket / MASK_BIT] & (EMH_MASK(bucket))) != 0 + +#if _WIN32 + #include <intrin.h> +#if _WIN64 + #pragma intrinsic(_umul128) +#endif +#endif + +namespace emhash7 { + +#ifdef EMH_SIZE_TYPE_16BIT + typedef uint16_t size_type; + static constexpr size_type INACTIVE = 0xFFFF; +#elif EMH_SIZE_TYPE_64BIT + typedef uint64_t size_type; + static constexpr size_type INACTIVE = 0 - 0x1ull; +#else + typedef uint32_t size_type; + static constexpr size_type INACTIVE = 0 - 0x1u; +#endif + +#ifndef EMH_SIZE_TYPE_16BIT +static_assert((int)INACTIVE < 0, "INACTIVE must negative (to int)"); +#endif + +//count the leading zero bit +inline static int CTZ(size_t n) +{ +#if defined(__x86_64__) || defined(_WIN32) || (__BYTE_ORDER__ && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + +#elif __BIG_ENDIAN__ || (__BYTE_ORDER__ && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) + n = __builtin_bswap64(n); +#else + static uint32_t endianness = 0x12345678; + const auto is_big = *(const char *)&endianness == 0x12; + if (is_big) + n = __builtin_bswap64(n); +#endif + +#if _WIN32 + unsigned long index; + #if defined(_WIN64) + _BitScanForward64(&index, n); + #else + _BitScanForward(&index, n); + #endif +#elif defined (__LP64__) || (SIZE_MAX == UINT64_MAX) || defined (__x86_64__) + auto index = __builtin_ctzll(n); +#elif 1 + auto index = __builtin_ctzl(n); +#else + #if defined (__LP64__) || (SIZE_MAX == UINT64_MAX) || defined (__x86_64__) + size_type index; + __asm__("bsfq %1, %0\n" : "=r" (index) : "rm" (n) : "cc"); + #else + size_type index; + __asm__("bsf %1, %0\n" : "=r" (index) : "rm" (n) : "cc"); + #endif +#endif + + return (int)index; +} + +template <typename First, typename Second> +struct entry { + using first_type = First; + using second_type = Second; + entry(const First& key, const Second& val, size_type ibucket) + :second(val), first(key) + { + bucket = ibucket; + } + + entry(First&& key, Second&& val, size_type ibucket) + :second(std::move(val)), first(std::move(key)) + { + bucket = ibucket; + } + + template<typename K, typename V> + entry(K&& key, V&& val, size_type ibucket) + :second(std::forward<V>(val)), first(std::forward<K>(key)) + { + bucket = ibucket; + } + + entry(const std::pair<First, Second>& pair) + :second(pair.second), first(pair.first) + { + bucket = INACTIVE; + } + + entry(std::pair<First, Second>&& pair) + :second(std::move(pair.second)), first(std::move(pair.first)) + { + bucket = INACTIVE; + } + + entry(std::tuple<First, Second>&& tup) + :second(std::move(std::get<2>(tup))), first(std::move(std::get<1>(tup))) + { + bucket = INACTIVE; + } + + entry(const entry& rhs) + :second(rhs.second), first(rhs.first) + { + bucket = rhs.bucket; + } + + entry(entry&& rhs) noexcept + :second(std::move(rhs.second)), first(std::move(rhs.first)) + { + bucket = rhs.bucket; + } + + entry& operator = (entry&& rhs) noexcept + { + second = std::move(rhs.second); + bucket = rhs.bucket; + first = std::move(rhs.first); + return *this; + } + + entry& operator = (const entry& rhs) + { + second = rhs.second; + bucket = rhs.bucket; + first = rhs.first; + return *this; + } + + bool operator == (const entry<First, Second>& p) const + { + return first == p.first && second == p.second; + } + + bool operator == (const std::pair<First, Second>& p) const + { + return first == p.first && second == p.second; + } + + void swap(entry<First, Second>& o) + { + std::swap(second, o.second); + std::swap(first, o.first); + } + +#if EMH_ORDER_KV || EMH_SIZE_TYPE_64BIT + First first; + size_type bucket; + Second second; +#else + Second second; + size_type bucket; + First first; +#endif +}; + +/// A cache-friendly hash table with open addressing, linear/qua probing and power-of-two capacity +template <typename KeyT, typename ValueT, typename HashT = std::hash<KeyT>, typename EqT = std::equal_to<KeyT>> +class HashMap +{ +#ifndef EMH_DEFAULT_LOAD_FACTOR + constexpr static float EMH_DEFAULT_LOAD_FACTOR = 0.80f; + constexpr static float EMH_MIN_LOAD_FACTOR = 0.25f; //< 0.5 +#endif + +public: + typedef HashMap<KeyT, ValueT, HashT, EqT> htype; + typedef std::pair<KeyT, ValueT> value_type; + +#if EMH_BUCKET_INDEX == 0 + typedef value_type value_pair; + typedef std::pair<size_type, value_type> PairT; +#elif EMH_BUCKET_INDEX == 2 + typedef value_type value_pair; + typedef std::pair<value_type, size_type> PairT; +#else + typedef entry<KeyT, ValueT> value_pair; + typedef entry<KeyT, ValueT> PairT; +#endif + + typedef KeyT key_type; + typedef ValueT val_type; + typedef ValueT mapped_type; + typedef HashT hasher; + typedef EqT key_equal; + typedef PairT& reference; + typedef const PairT& const_reference; + + class const_iterator; + class iterator + { + public: + typedef std::forward_iterator_tag iterator_category; + typedef std::ptrdiff_t difference_type; + typedef value_pair value_type; + + typedef value_pair* pointer; + typedef value_pair& reference; + + iterator() = default; + iterator(const const_iterator& it) : _map(it._map), _bucket(it._bucket), _from(it._from), _bmask(it._bmask) { } + iterator(const htype* hash_map, size_type bucket, bool) : _map(hash_map), _bucket(bucket) { init(); } +#if EMH_ITER_SAFE + iterator(const htype* hash_map, size_type bucket) : _map(hash_map), _bucket(bucket) { init(); } +#else + iterator(const htype* hash_map, size_type bucket) : _map(hash_map), _bucket(bucket) { _bmask = _from = 0; } +#endif + + void init() + { + _from = (_bucket / SIZE_BIT) * SIZE_BIT; + if (_bucket < _map->bucket_count()) { + _bmask = *(size_t*)((size_t*)_map->_bitmask + _from / SIZE_BIT); + _bmask |= (1ull << _bucket % SIZE_BIT) - 1; + _bmask = ~_bmask; + } else { + _bmask = 0; + } + } + + size_type bucket() const + { + return _bucket; + } + + void clear(size_type bucket) + { + if (_bucket / SIZE_BIT == bucket / SIZE_BIT) + _bmask &= ~(1ull << (bucket % SIZE_BIT)); + } + + iterator& next() + { + goto_next_element(); + return *this; + } + + iterator& operator++() + { + _bmask &= _bmask - 1; + goto_next_element(); + return *this; + } + + iterator operator++(int) + { + iterator old = *this; + _bmask &= _bmask - 1; + goto_next_element(); + return old; + } + + reference operator*() const + { + return _map->EMH_PKV(_pairs, _bucket); + } + + pointer operator->() const + { + return &(_map->EMH_PKV(_pairs, _bucket)); + } + + bool operator==(const iterator& rhs) const { return _bucket == rhs._bucket; } + bool operator!=(const iterator& rhs) const { return _bucket != rhs._bucket; } + bool operator==(const const_iterator& rhs) const { return _bucket == rhs._bucket; } + bool operator!=(const const_iterator& rhs) const { return _bucket != rhs._bucket; } + + private: + void goto_next_element() + { + if (EMH_LIKELY(_bmask != 0)) { + _bucket = _from + CTZ(_bmask); + return; + } + + do { + _bmask = ~*(size_t*)((size_t*)_map->_bitmask + (_from += SIZE_BIT) / SIZE_BIT); + } while (_bmask == 0); + + _bucket = _from + CTZ(_bmask); + } + + public: + const htype* _map; + size_type _bucket; + size_type _from; + size_t _bmask; + }; + + class const_iterator + { + public: + typedef std::forward_iterator_tag iterator_category; + typedef std::ptrdiff_t difference_type; + typedef value_pair value_type; + + typedef const value_pair* pointer; + typedef const value_pair& reference; + + const_iterator(const iterator& it) : _map(it._map), _bucket(it._bucket), _from(it._from), _bmask(it._bmask) { } + const_iterator(const htype* hash_map, size_type bucket, bool) : _map(hash_map), _bucket(bucket) { init(); } +#if EMH_ITER_SAFE + const_iterator(const htype* hash_map, size_type bucket) : _map(hash_map), _bucket(bucket) { init(); } +#else + const_iterator(const htype* hash_map, size_type bucket) : _map(hash_map), _bucket(bucket) { _bmask = _from = 0; } +#endif + + void init() + { + _from = (_bucket / SIZE_BIT) * SIZE_BIT; + if (_bucket < _map->bucket_count()) { + _bmask = *(size_t*)((size_t*)_map->_bitmask + _from / SIZE_BIT); + _bmask |= (1ull << _bucket % SIZE_BIT) - 1; + _bmask = ~_bmask; + } else { + _bmask = 0; + } + } + + size_type bucket() const + { + return _bucket; + } + + const_iterator& operator++() + { + goto_next_element(); + return *this; + } + + const_iterator operator++(int) + { + const_iterator old(*this); + goto_next_element(); + return old; + } + + reference operator*() const + { + return _map->EMH_PKV(_pairs, _bucket); + } + + pointer operator->() const + { + return &(_map->EMH_PKV(_pairs, _bucket)); + } + + bool operator==(const const_iterator& rhs) const { return _bucket == rhs._bucket; } + bool operator!=(const const_iterator& rhs) const { return _bucket != rhs._bucket; } + + private: + void goto_next_element() + { + _bmask &= _bmask - 1; + if (EMH_LIKELY(_bmask != 0)) { + _bucket = _from + CTZ(_bmask); + return; + } + + do { + _bmask = ~*(size_t*)((size_t*)_map->_bitmask + (_from += SIZE_BIT) / SIZE_BIT); + } while (_bmask == 0); + + _bucket = _from + CTZ(_bmask); + } + + public: + const htype* _map; + size_type _bucket; + size_type _from; + size_t _bmask; + }; + + void init(size_type bucket, float mlf = EMH_DEFAULT_LOAD_FACTOR) + { + _pairs = nullptr; + _bitmask = nullptr; + _num_buckets = _num_filled = 0; + max_load_factor(mlf); + rehash(bucket); + } + + HashMap(size_type bucket = 2, float mlf = EMH_DEFAULT_LOAD_FACTOR) noexcept + { + init(bucket, mlf); + } + + size_t AllocSize(uint64_t num_buckets) const + { + return (num_buckets + EPACK_SIZE) * sizeof(PairT) + (num_buckets + 7) / 8 + BIT_PACK; + } + + HashMap(const HashMap& rhs) noexcept + { + if (rhs.load_factor() > EMH_MIN_LOAD_FACTOR) { + _pairs = (PairT*)malloc(AllocSize(rhs._num_buckets)); + clone(rhs); + } else { + init(rhs._num_filled + 2, EMH_DEFAULT_LOAD_FACTOR); + for (auto it = rhs.begin(); it != rhs.end(); ++it) + insert_unique(it->first, it->second); + } + } + + HashMap(HashMap&& rhs) noexcept + { +#ifndef EMH_ZERO_MOVE + init(4); +#else + _num_buckets = _num_filled = _mask = 0; + _pairs = nullptr; +#endif + swap(rhs); + } + + HashMap(std::initializer_list<value_type> ilist) + { + init((size_type)ilist.size()); + for (auto it = ilist.begin(); it != ilist.end(); ++it) + do_insert(*it); + } + + template<class InputIt> + HashMap(InputIt first, InputIt last, size_type bucket_count=4) + { + init(std::distance(first, last) + bucket_count); + for (; first != last; ++first) + emplace(*first); + } + + HashMap& operator= (const HashMap& rhs) noexcept + { + if (this == &rhs) + return *this; + + if (rhs.load_factor() < EMH_MIN_LOAD_FACTOR) { + clear(); free(_pairs); _pairs = nullptr; + rehash(rhs._num_filled + 2); + for (auto it = rhs.begin(); it != rhs.end(); ++it) + insert_unique(it->first, it->second); + return *this; + } + + if (_num_filled) + clearkv(); + + if (_num_buckets != rhs._num_buckets) { + free(_pairs); + _pairs = (PairT*)malloc(AllocSize(rhs._num_buckets)); + } + + clone(rhs); + return *this; + } + + HashMap& operator= (HashMap&& rhs) noexcept + { + if (this != &rhs) { + swap(rhs); + rhs.clear(); + } + return *this; + } + + template<typename Con> + bool operator == (const Con& rhs) const + { + if (size() != rhs.size()) + return false; + + for (auto it = begin(), last = end(); it != last; ++it) { + auto oi = rhs.find(it->first); + if (oi == rhs.end() || it->second != oi->second) + return false; + } + return true; + } + + template<typename Con> + bool operator != (const Con& rhs) const { return !(*this == rhs); } + + ~HashMap() noexcept + { + if (is_triviall_destructable() && _num_filled) { + for (auto it = cbegin(); _num_filled; ++it) { + _num_filled --; + it->~value_pair(); + } + } + free(_pairs); + } + + void clone(const HashMap& rhs) noexcept + { + _hasher = rhs._hasher; + //_eq = rhs._eq; + + _num_filled = rhs._num_filled; + _mask = rhs._mask; + _mlf = rhs._mlf; + _num_buckets = rhs._num_buckets; + + _bitmask = decltype(_bitmask)(_pairs + EPACK_SIZE + _num_buckets); + auto* opairs = rhs._pairs; + + if (is_copy_trivially()) + memcpy(_pairs, opairs, AllocSize(_num_buckets)); + else { + memcpy(_pairs + _num_buckets, opairs + _num_buckets, EPACK_SIZE * sizeof(PairT) + (_num_buckets + 7) / 8 + BIT_PACK); + for (auto it = rhs.cbegin(); it.bucket() <= _mask; ++it) { + const auto bucket = it.bucket(); + EMH_BUCKET(_pairs, bucket) = EMH_BUCKET(opairs, bucket); + new(_pairs + bucket) PairT(opairs[bucket]); + } + } + } + + void swap(HashMap& rhs) + { + std::swap(_hasher, rhs._hasher); + //std::swap(_eq, rhs._eq); + std::swap(_pairs, rhs._pairs); + std::swap(_num_buckets, rhs._num_buckets); + std::swap(_num_filled, rhs._num_filled); + std::swap(_mask, rhs._mask); + std::swap(_mlf, rhs._mlf); + std::swap(_bitmask, rhs._bitmask); + } + + // ------------------------------------------------------------- + iterator begin() noexcept + { +#ifdef EMH_ZERO_MOVE + if (0 == _num_filled) + return {this, _num_buckets}; +#endif + + const auto bmask = ~(*(size_t*)_bitmask); + if (bmask != 0) + return {this, (size_type)CTZ(bmask), true}; + + iterator it(this, sizeof(bmask) * 8 - 1); + return it.next(); + } + + const_iterator cbegin() const noexcept + { +#ifdef EMH_ZERO_MOVE + if (0 == _num_filled) + return {this, _num_buckets}; +#endif + + const auto bmask = ~(*(size_t*)_bitmask); + if (bmask != 0) + return {this, (size_type)CTZ(bmask), true}; + + iterator it(this, sizeof(bmask) * 8 - 1); + return it.next(); + } + + iterator last() const + { + if (_num_filled == 0) + return end(); + + auto bucket = _mask; + while (EMH_EMPTY(_pairs, bucket)) bucket--; + return {this, bucket, true}; + } + + const_iterator begin() const noexcept { return cbegin(); } + + iterator end() noexcept { return {this, _num_buckets}; } + const_iterator cend() const { return {this, _num_buckets}; } + const_iterator end() const { return cend(); } + + size_type size() const { return _num_filled; } + bool empty() const { return _num_filled == 0; } + + size_type bucket_count() const { return _num_buckets; } + float load_factor() const { return static_cast<float>(_num_filled) / (_mask + 1); } + + HashT& hash_function() const { return _hasher; } + EqT& key_eq() const { return _eq; } + + void max_load_factor(float mlf) + { + if (mlf < 0.999f && mlf > EMH_MIN_LOAD_FACTOR) + _mlf = (uint32_t)((1 << 27) / mlf); + } + + constexpr float max_load_factor() const { return (1 << 27) / (float)_mlf; } + constexpr size_type max_size() const { return 1ull << (sizeof(size_type) * 8 - 1); } + constexpr size_type max_bucket_count() const { return max_size(); } + + size_type bucket_main() const + { + auto main_size = 0; + for (size_type bucket = 0; bucket < _num_buckets; ++bucket) { + if (EMH_BUCKET(_pairs, bucket) == bucket) + main_size ++; + } + return main_size; + } + +#if EMH_STATIS + //Returns the bucket number where the element with key k is located. + size_type bucket(const KeyT& key) const + { + const auto bucket = hash_key(key) & _mask; + const auto next_bucket = EMH_BUCKET(_pairs, bucket); + if (EMH_EMPTY(_pairs, bucket)) + return 0; + else if (bucket == next_bucket) + return bucket + 1; + + const auto& bucket_key = EMH_KEY(_pairs, bucket); + return (hash_key(bucket_key) & _mask) + 1; + } + + //Returns the number of elements in bucket n. + size_type bucket_size(const size_type bucket) const + { + if (EMH_EMPTY(_pairs, bucket)) + return 0; + + auto next_bucket = EMH_BUCKET(_pairs, bucket); + next_bucket = hash_key(EMH_KEY(_pairs, bucket)) & _mask; + size_type bucket_size = 1; + + //iterator each item in current main bucket + while (true) { + const auto nbucket = EMH_BUCKET(_pairs, next_bucket); + if (nbucket == next_bucket) { + break; + } + bucket_size++; + next_bucket = nbucket; + } + return bucket_size; + } + + size_type get_main_bucket(const size_type bucket) const + { + if (EMH_EMPTY(_pairs, bucket)) + return INACTIVE; + + auto next_bucket = EMH_BUCKET(_pairs, bucket); + const auto& bucket_key = EMH_KEY(_pairs, bucket); + const auto main_bucket = hash_key(bucket_key) & _mask; + return main_bucket; + } + + size_type get_diss(size_type bucket, size_type next_bucket, const size_type slots) const + { + const int cahe_line_size = 64; + auto pbucket = reinterpret_cast<uint64_t>(&_pairs[bucket]); + auto pnext = reinterpret_cast<uint64_t>(&_pairs[next_bucket]); + if (pbucket / cahe_line_size == pnext / cahe_line_size) + return 0; + size_type diff = pbucket > pnext ? (pbucket - pnext) : (pnext - pbucket); + if (diff / cahe_line_size + 1 < slots) + return (diff / cahe_line_size + 1); + return slots - 1; + } + + int get_bucket_info(const size_type bucket, size_type steps[], const size_type slots) const + { + if (EMH_EMPTY(_pairs, bucket)) + return -1; + + auto next_bucket = EMH_BUCKET(_pairs, bucket); + if ((hash_key(EMH_KEY(_pairs, bucket)) & _mask) != bucket) + return 0; + else if (next_bucket == bucket) + return 1; + + steps[get_diss(bucket, next_bucket, slots)] ++; + size_type bucket_size = 2; + while (true) { + const auto nbucket = EMH_BUCKET(_pairs, next_bucket); + if (nbucket == next_bucket) + break; + + steps[get_diss(nbucket, next_bucket, slots)] ++; + bucket_size ++; + next_bucket = nbucket; + } + + return bucket_size; + } + + void dump_statics(bool show_cache) const + { + const int slots = 128; + size_type buckets[slots + 1] = {0}; + size_type steps[slots + 1] = {0}; + char buff[1024 * 8]; + for (size_type bucket = 0; bucket < _num_buckets; ++bucket) { + auto bsize = get_bucket_info(bucket, steps, slots); + if (bsize >= 0) + buckets[bsize] ++; + } + + size_type sumb = 0, sums = 0, sumn = 0; + size_type miss = 0, finds = 0, bucket_coll = 0; + double lf = load_factor(), fk = 1.0 / exp(lf), sum_poisson = 0; + int bsize = sprintf (buff, "============== buckets size ration ========\n"); + + miss += _num_buckets - _num_filled; + for (int i = 1, factorial = 1; i < sizeof(buckets) / sizeof(buckets[0]); i++) { + double poisson = fk / factorial; factorial *= i; fk *= lf; + if (poisson > 1e-13 && i < 20) + sum_poisson += poisson * 100.0 * (i - 1) / i; + + const int64_t bucketsi = buckets[i]; + if (bucketsi == 0) + continue; + + sumb += bucketsi; + sumn += bucketsi * i; + bucket_coll += bucketsi * (i - 1); + finds += bucketsi * i * (i + 1) / 2; + miss += bucketsi * i * i; + auto errs = (bucketsi * 1.0 * i / _num_filled - poisson) * 100 / poisson; + bsize += sprintf(buff + bsize, " %2d %8ld %0.8lf|%0.2lf%% %2.3lf\n", + i, bucketsi, bucketsi * 1.0 * i / _num_filled, errs, sumn * 100.0 / _num_filled); + if (sumn >= _num_filled) + break; + } + + bsize += sprintf(buff + bsize, "========== collision miss ration ===========\n"); + for (size_type i = 0; show_cache && i < sizeof(steps) / sizeof(steps[0]); i++) { + sums += steps[i]; + if (steps[i] == 0) + continue; + if (steps[i] > 10) + bsize += sprintf(buff + bsize, " %2d %8u %0.2lf %.2lf\n", (int)i, steps[i], steps[i] * 100.0 / bucket_coll, sums * 100.0 / bucket_coll); + } + + if (sumb == 0) return; + + bsize += sprintf(buff + bsize, " _num_filled aver_size k.v size_kv = %u, %.2lf, %s.%s %zd\n", + _num_filled, _num_filled * 1.0 / sumb, typeid(KeyT).name(), typeid(ValueT).name(), sizeof(PairT)); + + bsize += sprintf(buff + bsize, " collision, poisson, cache_miss hit_find|hit_miss, load_factor = %.2lf%%,%.2lf%%,%.2lf%% %.2lf|%.2lf, %.2lf\n", + (bucket_coll * 100.0 / _num_filled), sum_poisson, (bucket_coll - steps[0]) * 100.0 / _num_filled, + finds * 1.0 / _num_filled, miss * 1.0 / _num_buckets, _num_filled * 1.0 / _num_buckets); + + bsize += sprintf(buff + bsize, "============== buckets size end =============\n"); + buff[bsize + 1] = 0; + +#ifdef EMH_LOG + EMH_LOG << __FUNCTION__ << "|" << buff << endl; +#else + puts(buff); +#endif + assert(sumn == _num_filled); + assert(sums == bucket_coll || !show_cache); + assert(bucket_coll == buckets[0]); + } +#endif + + // ------------------------------------------------------------ + template<typename Key = KeyT> + iterator find(const Key& key, size_t key_hash) noexcept + { + return {this, find_filled_hash(key, key_hash)}; + } + + template<typename Key = KeyT> + const_iterator find(const Key& key, size_t key_hash) const noexcept + { + return {this, find_filled_hash(key, key_hash)}; + } + + template<typename Key=KeyT> + iterator find(const Key& key) noexcept + { + return {this, find_filled_bucket(key)}; + } + + template<typename Key = KeyT> + const_iterator find(const Key& key) const noexcept + { + return {this, find_filled_bucket(key)}; + } + + template<typename Key = KeyT> + ValueT& at(const KeyT& key) + { + const auto bucket = find_filled_bucket(key); + //throw + return EMH_VAL(_pairs, bucket); + } + + template<typename Key = KeyT> + const ValueT& at(const KeyT& key) const + { + const auto bucket = find_filled_bucket(key); + //throw + return EMH_VAL(_pairs, bucket); + } + + template<typename Key = KeyT> + bool contains(const Key& key) const noexcept + { + return find_filled_bucket(key) != _num_buckets; + } + + template<typename Key = KeyT> + size_type count(const Key& key) const noexcept + { + return find_filled_bucket(key) != _num_buckets ? 1 : 0; + } + + template<typename Key = KeyT> + std::pair<iterator, iterator> equal_range(const Key& key) const noexcept + { + const auto found = {this, find_filled_bucket(key), true}; + if (found.bucket() == _num_buckets) + return { found, found }; + else + return { found, std::next(found) }; + } + + template<typename K=KeyT> + std::pair<const_iterator, const_iterator> equal_range(const K& key) const + { + const auto found = {this, find_filled_bucket(key), true}; + if (found.bucket() == _num_buckets) + return { found, found }; + else + return { found, std::next(found) }; + } + + void merge(HashMap& rhs) + { + if (empty()) { + *this = std::move(rhs); + return; + } + + for (auto rit = rhs.begin(); rit != rhs.end(); ) { + auto fit = find(rit->first); + if (fit.bucket() == _num_buckets) { + insert_unique(rit->first, std::move(rit->second)); + rit = rhs.erase(rit); + } else { + ++rit; + } + } + } + +#ifdef EMH_EXT + bool try_get(const KeyT& key, ValueT& val) const noexcept + { + const auto bucket = find_filled_bucket(key); + const auto found = bucket != _num_buckets; + if (found) { + val = EMH_VAL(_pairs, bucket); + } + return found; + } + + /// Returns the matching ValueT or nullptr if k isn't found. + ValueT* try_get(const KeyT& key) noexcept + { + const auto bucket = find_filled_bucket(key); + return bucket == _num_buckets ? nullptr : &EMH_VAL(_pairs, bucket); + } + + /// Const version of the above + ValueT* try_get(const KeyT& key) const noexcept + { + const auto bucket = find_filled_bucket(key); + return bucket == _num_buckets ? nullptr : &EMH_VAL(_pairs, bucket); + } + + /// Convenience function. + ValueT get_or_return_default(const KeyT& key) const noexcept + { + const auto bucket = find_filled_bucket(key); + return bucket == _num_buckets ? ValueT() : EMH_VAL(_pairs, bucket); + } +#endif + + // ----------------------------------------------------- + template<typename K = KeyT, typename V = ValueT> + std::pair<iterator, bool> do_assign(K&& key, V&& val) + { + reserve(_num_filled); + + bool isempty; + const auto bucket = find_or_allocate(key, isempty); + if (isempty) { + EMH_NEW(std::forward<K>(key), std::forward<V>(val), bucket); + } else { + EMH_VAL(_pairs, bucket) = std::move(val); + } + return { {this, bucket}, isempty }; + } + + std::pair<iterator, bool> do_insert(const value_type& value) + { + bool isempty; + const auto bucket = find_or_allocate(value.first, isempty); + if (isempty) { + EMH_NEW(value.first, value.second, bucket); + } + return { {this, bucket}, isempty }; + } + + std::pair<iterator, bool> do_insert(value_type&& value) + { + bool isempty; + const auto bucket = find_or_allocate(value.first, isempty); + if (isempty) { + EMH_NEW(std::move(value.first), std::move(value.second), bucket); + } + return { {this, bucket}, isempty }; + } + + template<typename K = KeyT, typename V = ValueT> + std::pair<iterator, bool> do_insert(K&& key, V&& val) + { + bool isempty; + const auto bucket = find_or_allocate(key, isempty); + if (isempty) { + EMH_NEW(std::forward<K>(key), std::forward<V>(val), bucket); + } + return { {this, bucket}, isempty }; + } + + std::pair<iterator, bool> insert(const value_type& value) + { + check_expand_need(); + return do_insert(value); + } + + std::pair<iterator, bool> insert(value_type&& value) + { + check_expand_need(); + return do_insert(std::move(value)); + } + + void insert(std::initializer_list<value_type> ilist) + { + reserve(ilist.size() + _num_filled); + for (auto it = ilist.begin(); it != ilist.end(); ++it) + do_insert(*it); + } + + template <typename Iter> + void insert(Iter first, Iter last) + { + reserve(std::distance(first, last) + _num_filled); + for (auto it = first; it != last; ++it) + do_insert(it->first, it->second); + } + +#if 0 + template <typename Iter> + void insert_unique(Iter begin, Iter end) + { + reserve(std::distance(begin, end) + _num_filled); + for (; begin != end; ++begin) + do_insert_unqiue(*begin); + } +#endif + + template<typename K, typename V> + size_type insert_unique(K&& key, V&& val) + { + return do_insert_unqiue(std::forward<K>(key), std::forward<V>(val)); + } + + size_type insert_unique(value_type&& value) + { + return do_insert_unqiue(std::move(value.first), std::move(value.second)); + } + + size_type insert_unique(const value_type& value) + { + return do_insert_unqiue(value.first, value.second); + } + + template<typename K, typename V> + inline size_type do_insert_unqiue(K&& key, V&& val) + { + check_expand_need(); + auto bucket = find_unique_bucket(key); + EMH_NEW(std::forward<K>(key), std::forward<V>(val), bucket); + return bucket; + } + + std::pair<iterator, bool> insert_or_assign(const KeyT& key, ValueT&& val) { return do_assign(key, std::forward<ValueT>(val)); } + std::pair<iterator, bool> insert_or_assign(KeyT&& key, ValueT&& val) { return do_assign(std::move(key), std::forward<ValueT>(val)); } + + template <typename... Args> + inline std::pair<iterator, bool> emplace(Args&&... args) noexcept + { + check_expand_need(); + return do_insert(std::forward<Args>(args)...); + } + + template <class... Args> + iterator emplace_hint(const_iterator hint, Args&&... args) + { + (void)hint; + check_expand_need(); + return do_insert(std::forward<Args>(args)...).first; + } + + template<class... Args> + std::pair<iterator, bool> try_emplace(const KeyT& key, Args&&... args) + { + check_expand_need(); + return do_insert(key, std::forward<Args>(args)...); + } + + template<class... Args> + std::pair<iterator, bool> try_emplace(KeyT&& key, Args&&... args) + { + check_expand_need(); + return do_insert(std::forward<KeyT>(key), std::forward<Args>(args)...); + } + + template <class... Args> + inline size_type emplace_unique(Args&&... args) noexcept + { + return insert_unique(std::forward<Args>(args)...); + } + + /* Check if inserting a new value rather than overwriting an old entry */ + ValueT& operator[](const KeyT& key) noexcept + { + check_expand_need(); + + bool isempty; + const auto bucket = find_or_allocate(key, isempty); + if (isempty) { + EMH_NEW(key, std::move(ValueT()), bucket); + } + + return EMH_VAL(_pairs, bucket); + } + + ValueT& operator[](KeyT&& key) noexcept + { + check_expand_need(); + + bool isempty; + const auto bucket = find_or_allocate(key, isempty); + if (isempty) { + EMH_NEW(std::move(key), std::move(ValueT()), bucket); + } + + return EMH_VAL(_pairs, bucket); + } + + // ------------------------------------------------------- + /// Erase an element from the hash table. + /// return 0 if element was not found + template<typename Key = KeyT> + size_type erase(const Key& key) + { + const auto bucket = erase_key(key); + if (bucket == INACTIVE) + return 0; + + clear_bucket(bucket); + return 1; + } + + //iterator erase const_iterator + iterator erase(const_iterator cit) + { + iterator it(cit); + return erase(it); + } + + /// Erase an element typedef an iterator. + /// Returns an iterator to the next element (or end()). + iterator erase(iterator it) + { + const auto bucket = erase_bucket(it._bucket); + clear_bucket(bucket); + if (bucket == it._bucket) { + return ++it; + } else { + //erase main bucket as next + it.clear(bucket); + return it; + } + } + + /// Erase an element typedef an iterator without return next iterator + void _erase(const_iterator it) + { + const auto bucket = erase_bucket(it._bucket); + clear_bucket(bucket); + } + + template<typename Pred> + size_type erase_if(Pred pred) + { + auto old_size = size(); + for (auto it = begin(), last = end(); it != last; ) { + if (pred(*it)) + it = erase(it); + else + ++it; + } + return old_size - size(); + } + + static constexpr bool is_triviall_destructable() + { +#if __cplusplus >= 201402L || _MSC_VER > 1600 + return !(std::is_trivially_destructible<KeyT>::value && std::is_trivially_destructible<ValueT>::value); +#else + return !(std::is_pod<KeyT>::value && std::is_pod<ValueT>::value); +#endif + } + + static constexpr bool is_copy_trivially() + { +#if __cplusplus >= 201402L || _MSC_VER > 1600 + return (std::is_trivially_copyable<KeyT>::value && std::is_trivially_copyable<ValueT>::value); +#else + return (std::is_pod<KeyT>::value && std::is_pod<ValueT>::value); +#endif + } + + void clearkv() + { + if (is_triviall_destructable()) { + for (auto it = cbegin(); _num_filled; ++it) + clear_bucket(it.bucket()); + } + } + + /// Remove all elements, keeping full capacity. + void clear() + { + if (!is_triviall_destructable() && _num_filled) { + memset(_bitmask, 0xFFFFFFFF, (_num_buckets + 7) / 8); + if (_num_buckets < 8) _bitmask[0] = (1 << _num_buckets) - 1; + } + else if (_num_filled) + clearkv(); + + //EMH_BUCKET(_pairs, _num_buckets) = 0; //_last + _num_filled = 0; + } + + void shrink_to_fit() + { + rehash(_num_filled + 1); + } + + /// Make room for this many elements + bool reserve(uint64_t num_elems) + { + const auto required_buckets = (num_elems * _mlf >> 27); + if (EMH_LIKELY(required_buckets < _num_buckets)) + return false; + +#if EMH_HIGH_LOAD + if (required_buckets < 64 && _num_filled < _num_buckets) + return false; +#endif + +#if EMH_STATIS + if (_num_filled > EMH_STATIS) dump_statics(true); +#endif + rehash(required_buckets + 2); + return true; + } + + void rehash(uint64_t required_buckets) + { + if (required_buckets < _num_filled) + return; + + uint64_t buckets = _num_filled > (1u << 16) ? (1u << 16) : 2u; + while (buckets < required_buckets) { buckets *= 2; } + + // no need alloc large bucket for small key sizeof(KeyT) < sizeof(int). + // set small a max_load_factor, insert/reserve() will fail and introduce rehash issiue TODO: dothing ? + if (sizeof(KeyT) < sizeof(size_type) && buckets >= (1ul << (2 * 8))) + buckets = 2ul << (sizeof(KeyT) * 8); + + assert(buckets < max_size() && buckets > _num_filled); + //TODO: throwOverflowError + + auto num_buckets = (size_type)buckets; + auto old_num_filled = _num_filled; + auto old_mask = _num_buckets - 1; + auto old_pairs = _pairs; + auto* obmask = _bitmask; + + _num_filled = 0; + _num_buckets = num_buckets; + _mask = num_buckets - 1; + + _pairs = (PairT*)malloc(AllocSize(_num_buckets)); + memset((char*)(_pairs + _num_buckets), 0, sizeof(PairT) * EPACK_SIZE); + + _bitmask = decltype(_bitmask)(_pairs + EPACK_SIZE + num_buckets); + + const auto mask_byte = (num_buckets + 7) / 8; + memset(_bitmask, 0xFFFFFFFF, mask_byte); + memset(((char*)_bitmask) + mask_byte, 0, BIT_PACK); + if (num_buckets < 8) + _bitmask[0] = (1 << num_buckets) - 1; + + //for (size_type src_bucket = 0; _num_filled < old_num_filled; src_bucket++) { + for (size_type src_bucket = old_mask; _num_filled < old_num_filled; src_bucket --) { + if (obmask[src_bucket / MASK_BIT] & (EMH_MASK(src_bucket))) + continue; + + auto& key = EMH_KEY(old_pairs, src_bucket); + const auto bucket = find_unique_bucket(key); + EMH_NEW(std::move(key), std::move(EMH_VAL(old_pairs, src_bucket)), bucket); + if (is_triviall_destructable()) + old_pairs[src_bucket].~PairT(); + } + +#if EMH_REHASH_LOG + if (_num_filled > EMH_REHASH_LOG) { + auto mbucket = bucket_main(); + char buff[255] = {0}; + sprintf(buff, " _num_filled/collision/main/K.V/pack/ = %u/%.2lf%%(%.2lf%%)/%s.%s/%zd", + _num_filled, 200.0f * (_num_filled - mbucket) / _mask, 100.0f * mbucket / _mask, + typeid(KeyT).name(), typeid(ValueT).name(), sizeof(_pairs[0])); +#ifdef EMH_LOG + static size_t ihashs = 0; + EMH_LOG << "rhash_nums = " << ihashs ++ << "|" <<__FUNCTION__ << "|" << buff << endl; +#else + puts(buff); +#endif + } +#endif + + free(old_pairs); + assert(old_num_filled == _num_filled); + } + +private: + // Can we fit another element? + inline bool check_expand_need() + { + return reserve(_num_filled); + } + + void clear_bucket(size_type bucket) + { + EMH_CLS(bucket); + _num_filled--; + if (is_triviall_destructable()) + _pairs[bucket].~PairT(); + } + +#if 1 + //template<typename UType, typename std::enable_if<std::is_integral<UType>::value, size_type>::type = 0> + template<typename UType> + size_type erase_key(const UType& key) + { + const auto bucket = hash_key(key) & _mask; + if (EMH_EMPTY(_pairs, bucket)) + return INACTIVE; + + auto next_bucket = EMH_BUCKET(_pairs, bucket); + const auto eqkey = _eq(key, EMH_KEY(_pairs, bucket)); + if (eqkey) { + if (next_bucket == bucket) + return bucket; + + const auto nbucket = EMH_BUCKET(_pairs, next_bucket); + if (is_copy_trivially()) + EMH_PKV(_pairs, bucket) = EMH_PKV(_pairs, next_bucket); + else + EMH_PKV(_pairs, bucket).swap(EMH_PKV(_pairs, next_bucket)); + + EMH_BUCKET(_pairs, bucket) = (nbucket == next_bucket) ? bucket : nbucket; + return next_bucket; + } else if (next_bucket == bucket) + return INACTIVE; + /* else if (EMH_UNLIKELY(bucket != hash_key(EMH_KEY(_pairs, bucket)) & _mask)) + return INACTIVE; + */ + + auto prev_bucket = bucket; + while (true) { + const auto nbucket = EMH_BUCKET(_pairs, next_bucket); + if (_eq(key, EMH_KEY(_pairs, next_bucket))) { + EMH_BUCKET(_pairs, prev_bucket) = (nbucket == next_bucket) ? prev_bucket : nbucket; + return next_bucket; + } + + if (nbucket == next_bucket) + break; + prev_bucket = next_bucket; + next_bucket = nbucket; + } + + return INACTIVE; + } +#else + template<typename UType, typename std::enable_if<!std::is_integral<UType>::value, size_type>::type = 0> + size_type erase_key(const UType& key) + { + const auto bucket = hash_key(key) & _mask; + if (EMH_EMPTY(_pairs, bucket)) + return INACTIVE; + + auto next_bucket = EMH_BUCKET(_pairs, bucket); + if (next_bucket == bucket) + return _eq(key, EMH_KEY(_pairs, bucket)) ? bucket : INACTIVE; +// else if (bucket != hash_key(EMH_KEY(_pairs, bucket))) +// return INACTIVE; + + //find erase key and swap to last bucket + size_type prev_bucket = bucket, find_bucket = INACTIVE; + next_bucket = bucket; + while (true) { + const auto nbucket = EMH_BUCKET(_pairs, next_bucket); + if (_eq(key, EMH_KEY(_pairs, next_bucket))) { + find_bucket = next_bucket; + if (nbucket == next_bucket) { + EMH_BUCKET(_pairs, prev_bucket) = prev_bucket; + break; + } + } + if (nbucket == next_bucket) { + if (find_bucket != INACTIVE) { + EMH_PKV(_pairs, find_bucket).swap(EMH_PKV(_pairs, nbucket)); +// EMH_PKV(_pairs, find_bucket) = EMH_PKV(_pairs, nbucket); + EMH_BUCKET(_pairs, prev_bucket) = prev_bucket; + find_bucket = nbucket; + } + break; + } + prev_bucket = next_bucket; + next_bucket = nbucket; + } + + return find_bucket; + } +#endif + + size_type erase_bucket(const size_type bucket) + { + const auto next_bucket = EMH_BUCKET(_pairs, bucket); + const auto main_bucket = hash_key(EMH_KEY(_pairs, bucket)) & _mask; + if (bucket == main_bucket) { + if (bucket != next_bucket) { + const auto nbucket = EMH_BUCKET(_pairs, next_bucket); + if (is_copy_trivially()) + EMH_PKV(_pairs, bucket) = EMH_PKV(_pairs, next_bucket); + else + EMH_PKV(_pairs, bucket).swap(EMH_PKV(_pairs, next_bucket)); + EMH_BUCKET(_pairs, bucket) = (nbucket == next_bucket) ? bucket : nbucket; + } + return next_bucket; + } + + const auto prev_bucket = find_prev_bucket(main_bucket, bucket); + EMH_BUCKET(_pairs, prev_bucket) = (bucket == next_bucket) ? prev_bucket : next_bucket; + return bucket; + } + + // Find the bucket with this key, or return bucket size + template<typename K = KeyT> + size_type find_filled_hash(const K& key, const size_t key_hash) const + { + const auto bucket = key_hash & _mask; + if (EMH_EMPTY(_pairs, bucket)) + return _num_buckets; + + auto next_bucket = bucket; + while (true) { + if (_eq(key, EMH_KEY(_pairs, next_bucket))) + return next_bucket; + + const auto nbucket = EMH_BUCKET(_pairs, next_bucket); + if (nbucket == next_bucket) + break; + next_bucket = nbucket; + } + + return _num_buckets; + } + + // Find the bucket with this key, or return bucket size + template<typename K = KeyT> + size_type find_filled_bucket(const K& key) const + { + const auto bucket = hash_key(key) & _mask; + if (EMH_EMPTY(_pairs, bucket)) + return _num_buckets; + + auto next_bucket = bucket; +// else if (bucket != (hash_key(bucket_key) & _mask)) +// return _num_buckets; + + while (true) { + if (_eq(key, EMH_KEY(_pairs, next_bucket))) + return next_bucket; + + const auto nbucket = EMH_BUCKET(_pairs, next_bucket); + if (nbucket == next_bucket) + return _num_buckets; + next_bucket = nbucket; + } + + return 0; + } + + //kick out bucket and find empty to occpuy + //it will break the orgin link and relnik again. + //before: main_bucket-->prev_bucket --> bucket --> next_bucket + //atfer : main_bucket-->prev_bucket --> (removed)--> new_bucket--> next_bucket + size_type kickout_bucket(const size_type kmain, const size_type kbucket) + { + const auto next_bucket = EMH_BUCKET(_pairs, kbucket); + const auto new_bucket = find_empty_bucket(next_bucket, kbucket); + const auto prev_bucket = find_prev_bucket(kmain, kbucket); + new(_pairs + new_bucket) PairT(std::move(_pairs[kbucket])); + if (is_triviall_destructable()) + _pairs[kbucket].~PairT(); + + if (next_bucket == kbucket) + EMH_BUCKET(_pairs, new_bucket) = new_bucket; + EMH_BUCKET(_pairs, prev_bucket) = new_bucket; + + EMH_SET(new_bucket); + return kbucket; + } + +/* +** inserts a new key into a hash table; first check whether key's main +** bucket/position is free. If not, check whether colliding node/bucket is in its main +** position or not: if it is not, move colliding bucket to an empty place and +** put new key in its main position; otherwise (colliding bucket is in its main +** position), new key goes to an empty position. ***/ + + template<typename K=KeyT> + size_type find_or_allocate(const K& key, bool& isempty) + { + const auto bucket = hash_key(key) & _mask; + const auto& bucket_key = EMH_KEY(_pairs, bucket); + if (EMH_EMPTY(_pairs, bucket)) { + isempty = true; + return bucket; + } + else if (_eq(key, bucket_key)) { + isempty = false; + return bucket; + } + + isempty = true; + auto next_bucket = EMH_BUCKET(_pairs, bucket); + //check current bucket_key is in main bucket or not + const auto kmain_bucket = hash_key(bucket_key) & _mask; + if (kmain_bucket != bucket) + return kickout_bucket(kmain_bucket, bucket); + else if (next_bucket == bucket) + return EMH_BUCKET(_pairs, next_bucket) = find_empty_bucket(next_bucket, bucket); + +#if EMH_LRU_SET + auto prev_bucket = bucket; +#endif + //find next linked bucket and check key, if lru is set then swap current key with prev_bucket + while (true) { + if (EMH_UNLIKELY(_eq(key, EMH_KEY(_pairs, next_bucket)))) { + isempty = false; +#if EMH_LRU_SET + EMH_PKV(_pairs, next_bucket).swap(EMH_PKV(_pairs, prev_bucket)); + return prev_bucket; +#else + return next_bucket; +#endif + } + +#if EMH_LRU_SET + prev_bucket = next_bucket; +#endif + + const auto nbucket = EMH_BUCKET(_pairs, next_bucket); + if (nbucket == next_bucket) + break; + next_bucket = nbucket; + } + + //find a new empty and link it to tail, TODO link after main bucket? + const auto new_bucket = find_empty_bucket(next_bucket, bucket);// : find_empty_bucket(next_bucket); + return EMH_BUCKET(_pairs, next_bucket) = new_bucket; + } + + // key is not in this map. Find a place to put it. + size_type find_empty_bucket(const size_type bucket_from, const size_type main_bucket) + { +#ifdef EMH_ALIGN64 // only works 64bit + const auto boset = bucket_from % MASK_BIT; + auto* const align = _bitmask + bucket_from / MASK_BIT; + const auto bmask = ((size_t)align[1] << (MASK_BIT - boset)) | (align[0] >> boset); + if (EMH_LIKELY(bmask != 0)) + return bucket_from + CTZ(bmask); +#elif EMH_ITER_SAFE + const auto boset = bucket_from % 8; + auto* const start = (uint8_t*)_bitmask + bucket_from / 8; + size_t bmask; memcpy(&bmask, start + 0, sizeof(bmask)); bmask >>= boset;// bmask |= ((size_t)start[8] << (SIZE_BIT - boset)); + if (EMH_LIKELY(bmask != 0)) + return bucket_from + CTZ(bmask); +#else + const auto boset = main_bucket % 8; + auto* const align = (uint8_t*)_bitmask + main_bucket / 8; (void)bucket_from; + const size_t bmask = (*(size_t*)(align) >> boset);// & 0xF0F0F0F0FF0FF0FFull;// + if (EMH_LIKELY(bmask != 0)) + return main_bucket + CTZ(bmask); +#endif + + const auto qmask = _mask / SIZE_BIT; + if (1) { + const size_type step = (main_bucket - SIZE_BIT / 4) & qmask; + const auto bmask3 = *((size_t*)_bitmask + step); + if (bmask3 != 0) + return step * SIZE_BIT + CTZ(bmask3); + } + + for (; ;) { + auto& _last = EMH_BUCKET(_pairs, _num_buckets); + const auto bmask2 = *((size_t*)_bitmask + _last); + if (bmask2 != 0) + return _last * SIZE_BIT + CTZ(bmask2); + + const auto next1 = (qmask / 2 + _last) & qmask; + const auto bmask1 = *((size_t*)_bitmask + next1); + if (bmask1 != 0) { + //_last = next1; + return next1 * SIZE_BIT + CTZ(bmask1); + } + _last = (_last + 1) & qmask; + } + + return 0; + } + + // key is not in this map. Find a place to put it. + size_type find_unique_empty(const size_type bucket_from, const size_t main_bucket) + { +#ifdef EMH_ALIGN64 + const auto boset = bucket_from % MASK_BIT; + auto* const align = _bitmask + bucket_from / MASK_BIT; + const auto bmask = ((size_t)align[1] << (MASK_BIT - boset)) | (align[0] >> boset); + static_assert(sizeof(size_t) > 4); +#elif EMH_ITER_SAFE + const auto boset = bucket_from % 8; + auto* const start = (uint8_t*)_bitmask + bucket_from / 8; + size_t bmask; memcpy(&bmask, start + 0, sizeof(bmask)); bmask >>= boset; +#else + const auto boset = bucket_from % 8; (void)main_bucket; + auto* const align = (uint8_t*)_bitmask + bucket_from / 8; + const auto bmask = (*(size_t*)(align) >> boset); //maybe not aligned and warning +#endif + if (EMH_LIKELY(bmask != 0)) + return bucket_from + CTZ(bmask); + + const auto qmask = _mask / SIZE_BIT; + for (auto last = (bucket_from + _mask) & qmask; ;) { + const auto bmask2 = *((size_t*)_bitmask + last);// & 0xF0F0F0F0FF0FF0FFull; + if (EMH_LIKELY(bmask2 != 0)) + return last * SIZE_BIT + CTZ(bmask2); + last = (last + 1) & qmask; + } + + return 0; + } + + size_type find_last_bucket(size_type main_bucket) const + { + auto next_bucket = EMH_BUCKET(_pairs, main_bucket); + if (next_bucket == main_bucket) + return main_bucket; + + while (true) { + const auto nbucket = EMH_BUCKET(_pairs, next_bucket); + if (nbucket == next_bucket) + return next_bucket; + next_bucket = nbucket; + } + } + + size_type find_prev_bucket(size_type main_bucket, const size_type bucket) const + { + auto next_bucket = EMH_BUCKET(_pairs, main_bucket); + if (next_bucket == bucket) + return main_bucket; + + while (true) { + const auto nbucket = EMH_BUCKET(_pairs, next_bucket); + if (nbucket == bucket) + return next_bucket; + next_bucket = nbucket; + } + } + + size_type find_unique_bucket(const KeyT& key) + { + const size_type bucket = hash_key(key) & _mask; + if (EMH_EMPTY(_pairs, bucket)) + return bucket; + + //check current bucket_key is in main bucket or not + const auto kmain_bucket = hash_key(EMH_KEY(_pairs, bucket)) & _mask; + if (EMH_UNLIKELY(kmain_bucket != bucket)) + return kickout_bucket(kmain_bucket, bucket); + + auto next_bucket = EMH_BUCKET(_pairs, bucket); + if (next_bucket != bucket) + next_bucket = find_last_bucket(next_bucket); + + //find a new empty and link it to tail + return EMH_BUCKET(_pairs, next_bucket) = find_unique_empty(next_bucket, bucket); + } + +#if EMH_INT_HASH + static constexpr uint64_t KC = UINT64_C(11400714819323198485); + static inline uint64_t hash64(uint64_t key) + { +#if __SIZEOF_INT128__ && EMH_INT_HASH == 1 + __uint128_t r = key; r *= KC; + return (uint64_t)(r >> 64) + (uint64_t)r; +#elif EMH_INT_HASH == 2 + //MurmurHash3Mixer + uint64_t h = key; + h ^= h >> 33; + h *= 0xff51afd7ed558ccd; + h ^= h >> 33; + h *= 0xc4ceb9fe1a85ec53; + h ^= h >> 33; + return h; +#elif _WIN64 && EMH_INT_HASH == 1 + uint64_t high; + return _umul128(key, KC, &high) + high; +#elif EMH_INT_HASH == 3 + auto ror = (key >> 32) | (key << 32); + auto low = key * 0xA24BAED4963EE407ull; + auto high = ror * 0x9FB21C651E98DF25ull; + auto mix = low + high; + return mix; +#elif EMH_INT_HASH == 1 + uint64_t r = key * UINT64_C(0xca4bcaa75ec3f625); + return (r >> 32) + r; +#elif EMH_WYHASH64 + return wyhash64(key, KC); +#else + uint64_t x = key; + x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9); + x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb); + x = x ^ (x >> 31); + return x; +#endif + } +#endif + + template<typename UType, typename std::enable_if<std::is_integral<UType>::value, size_type>::type = 0> + inline size_type hash_key(const UType key) const + { +#if EMH_INT_HASH + return hash64(key); +#elif EMH_IDENTITY_HASH + return key + (key >> 24); +#else + return (size_type)_hasher(key); +#endif + } + + template<typename UType, typename std::enable_if<std::is_same<UType, std::string>::value, size_type>::type = 0> + inline size_type hash_key(const UType& key) const + { +#if EMH_WY_HASH + return wyhash(key.data(), key.size(), 0); +#else + return (size_type)_hasher(key); +#endif + } + + template<typename UType, typename std::enable_if<!std::is_integral<UType>::value && !std::is_same<UType, std::string>::value, size_type>::type = 0> + inline size_type hash_key(const UType& key) const + { + return (size_type)_hasher(key); + } + +private: + uint32_t* _bitmask; + PairT* _pairs; + HashT _hasher; + EqT _eq; + size_type _mask; + size_type _num_buckets; + + size_type _num_filled; + uint32_t _mlf; + +private: + static constexpr uint32_t BIT_PACK = sizeof(_bitmask[0]) * 2; + static constexpr uint32_t MASK_BIT = sizeof(_bitmask[0]) * 8; + static constexpr uint32_t SIZE_BIT = sizeof(size_t) * 8; + static constexpr uint32_t EPACK_SIZE = sizeof(PairT) >= sizeof(size_t) == 0 ? 1 : 2; // > 1 +}; +} +// namespace emhash7 +#if __cplusplus >= 201103L +//template <class Key, class Val> using ehmap7 = emhash7::HashMap<Key, Val, std::hash<Key>, std::equal_to<Key>>; +#endif + +//TODO +//2. improve rehash and find miss performance(reduce peak memory) +//3. dump or Serialization interface +//4. node hash map support +//5. load_factor > 1.0 && add grow ration +//... https://godbolt.org/ diff --git a/misc/benchmarks/external/khash.h b/misc/benchmarks/external/khash.h new file mode 100644 index 00000000..61dabc4d --- /dev/null +++ b/misc/benchmarks/external/khash.h @@ -0,0 +1,595 @@ +/* The MIT License + Copyright (c) 2008, 2009, 2011 by Attractive Chaos <[email protected]> + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +/* + An example: +#include "khash.h" +KHASH_MAP_INIT_INT(32, char) +int main() { + int ret, is_missing; + khiter_t k; + khash_t(32) *h = kh_init(32); + k = kh_put(32, h, 5, &ret); + kh_value(h, k) = 10; + k = kh_get(32, h, 10); + is_missing = (k == kh_end(h)); + k = kh_get(32, h, 5); + kh_del(32, h, k); + for (k = kh_begin(h); k != kh_end(h); ++k) + if (kh_exist(h, k)) kh_value(h, k) = 1; + kh_destroy(32, h); + return 0; +} +*/ + +/* + 2013-05-02 (0.2.8): + * Use quadratic probing. When the capacity is power of 2, stepping function + i*(i+1)/2 guarantees to traverse each bucket. It is better than double + hashing on cache performance and is more robust than linear probing. + In theory, double hashing should be more robust than quadratic probing. + However, my implementation is probably not for large hash tables, because + the second hash function is closely tied to the first hash function, + which reduce the effectiveness of double hashing. + Reference: http://research.cs.vt.edu/AVresearch/hashing/quadratic.php + 2011-12-29 (0.2.7): + * Minor code clean up; no actual effect. + 2011-09-16 (0.2.6): + * The capacity is a power of 2. This seems to dramatically improve the + speed for simple keys. Thank Zilong Tan for the suggestion. Reference: + - http://code.google.com/p/ulib/ + - http://nothings.org/computer/judy/ + * Allow to optionally use linear probing which usually has better + performance for random input. Double hashing is still the default as it + is more robust to certain non-random input. + * Added Wang's integer hash function (not used by default). This hash + function is more robust to certain non-random input. + 2011-02-14 (0.2.5): + * Allow to declare global functions. + 2009-09-26 (0.2.4): + * Improve portability + 2008-09-19 (0.2.3): + * Corrected the example + * Improved interfaces + 2008-09-11 (0.2.2): + * Improved speed a little in kh_put() + 2008-09-10 (0.2.1): + * Added kh_clear() + * Fixed a compiling error + 2008-09-02 (0.2.0): + * Changed to token concatenation which increases flexibility. + 2008-08-31 (0.1.2): + * Fixed a bug in kh_get(), which has not been tested previously. + 2008-08-31 (0.1.1): + * Added destructor +*/ + + +#ifndef __AC_KHASH_H +#define __AC_KHASH_H + +/*! + @header + Generic hash table library. + */ + +#define AC_VERSION_KHASH_H "0.2.8" + +#include <stdlib.h> +#include <string.h> +#include <limits.h> + +/* compiler specific configuration */ + +#if UINT_MAX == 0xffffffffu +typedef unsigned int khint32_t; +#elif ULONG_MAX == 0xffffffffu +typedef unsigned long khint32_t; +#endif + +#if ULONG_MAX == ULLONG_MAX +typedef unsigned long khint64_t; +#else +typedef unsigned long long khint64_t; +#endif + +#ifndef kh_inline +#ifdef _MSC_VER +#define kh_inline __inline +#else +#define kh_inline inline +#endif +#endif /* kh_inline */ + +#ifndef klib_unused +#if (defined __clang__ && __clang_major__ >= 3) || (defined __GNUC__ && __GNUC__ >= 3) +#define klib_unused __attribute__ ((__unused__)) +#else +#define klib_unused +#endif +#endif /* klib_unused */ + +typedef khint32_t khint_t; +typedef khint_t khiter_t; + +#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2) +#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1) +#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3) +#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1))) +#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1))) +#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1))) +#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1)) + +#define __ac_fsize(m) ((m) < 16? 1 : (m)>>4) + +#ifndef kroundup32 +#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x)) +#endif + +#ifndef kcalloc +#define kcalloc(N,Z) calloc(N,Z) +#endif +#ifndef kmalloc +#define kmalloc(Z) malloc(Z) +#endif +#ifndef krealloc +#define krealloc(P,Z) realloc(P,Z) +#endif +#ifndef kfree +#define kfree(P) free(P) +#endif + +static const double __ac_HASH_UPPER = 0.77; + +#define __KHASH_TYPE(name, khkey_t, khval_t) \ + typedef struct kh_##name##_s { \ + khint_t n_buckets, size, n_occupied, upper_bound; \ + khint32_t *flags; \ + khkey_t *keys; \ + khval_t *vals; \ + } kh_##name##_t; + +#define __KHASH_PROTOTYPES(name, khkey_t, khval_t) \ + extern kh_##name##_t *kh_init_##name(void); \ + extern void kh_destroy_##name(kh_##name##_t *h); \ + extern void kh_clear_##name(kh_##name##_t *h); \ + extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); \ + extern int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \ + extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \ + extern void kh_del_##name(kh_##name##_t *h, khint_t x); + +#define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ + SCOPE kh_##name##_t *kh_init_##name(void) { \ + return (kh_##name##_t*)kcalloc(1, sizeof(kh_##name##_t)); \ + } \ + SCOPE void kh_destroy_##name(kh_##name##_t *h) \ + { \ + if (h) { \ + kfree((void *)h->keys); kfree(h->flags); \ + kfree((void *)h->vals); \ + kfree(h); \ + } \ + } \ + SCOPE void kh_clear_##name(kh_##name##_t *h) \ + { \ + if (h && h->flags) { \ + memset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \ + h->size = h->n_occupied = 0; \ + } \ + } \ + SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \ + { \ + if (h->n_buckets) { \ + khint_t k, i, last, mask, step = 0; \ + mask = h->n_buckets - 1; \ + k = __hash_func(key); i = k & mask; \ + last = i; \ + while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \ + i = (i + (++step)) & mask; \ + if (i == last) return h->n_buckets; \ + } \ + return __ac_iseither(h->flags, i)? h->n_buckets : i; \ + } else return 0; \ + } \ + SCOPE int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \ + { /* This function uses 0.25*n_buckets bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \ + khint32_t *new_flags = 0; \ + khint_t j = 1; \ + { \ + kroundup32(new_n_buckets); \ + if (new_n_buckets < 4) new_n_buckets = 4; \ + if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0; /* requested size is too small */ \ + else { /* hash table size to be changed (shrink or expand); rehash */ \ + new_flags = (khint32_t*)kmalloc(__ac_fsize(new_n_buckets) * sizeof(khint32_t)); \ + if (!new_flags) return -1; \ + memset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \ + if (h->n_buckets < new_n_buckets) { /* expand */ \ + khkey_t *new_keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \ + if (!new_keys) { kfree(new_flags); return -1; } \ + h->keys = new_keys; \ + if (kh_is_map) { \ + khval_t *new_vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \ + if (!new_vals) { kfree(new_flags); return -1; } \ + h->vals = new_vals; \ + } \ + } /* otherwise shrink */ \ + } \ + } \ + if (j) { /* rehashing is needed */ \ + for (j = 0; j != h->n_buckets; ++j) { \ + if (__ac_iseither(h->flags, j) == 0) { \ + khkey_t key = h->keys[j]; \ + khval_t val; \ + khint_t new_mask; \ + new_mask = new_n_buckets - 1; \ + if (kh_is_map) val = h->vals[j]; \ + __ac_set_isdel_true(h->flags, j); \ + while (1) { /* kick-out process; sort of like in Cuckoo hashing */ \ + khint_t k, i, step = 0; \ + k = __hash_func(key); \ + i = k & new_mask; \ + while (!__ac_isempty(new_flags, i)) i = (i + (++step)) & new_mask; \ + __ac_set_isempty_false(new_flags, i); \ + if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \ + { khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \ + if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \ + __ac_set_isdel_true(h->flags, i); /* mark it as deleted in the old hash table */ \ + } else { /* write the element and jump out of the loop */ \ + h->keys[i] = key; \ + if (kh_is_map) h->vals[i] = val; \ + break; \ + } \ + } \ + } \ + } \ + if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \ + h->keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \ + if (kh_is_map) h->vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \ + } \ + kfree(h->flags); /* free the working space */ \ + h->flags = new_flags; \ + h->n_buckets = new_n_buckets; \ + h->n_occupied = h->size; \ + h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \ + } \ + return 0; \ + } \ + SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \ + { \ + khint_t x; \ + if (h->n_occupied >= h->upper_bound) { /* update the hash table */ \ + if (h->n_buckets > (h->size<<1)) { \ + if (kh_resize_##name(h, h->n_buckets - 1) < 0) { /* clear "deleted" elements */ \ + *ret = -1; return h->n_buckets; \ + } \ + } else if (kh_resize_##name(h, h->n_buckets + 1) < 0) { /* expand the hash table */ \ + *ret = -1; return h->n_buckets; \ + } \ + } /* TODO: to implement automatically shrinking; resize() already support shrinking */ \ + { \ + khint_t k, i, site, last, mask = h->n_buckets - 1, step = 0; \ + x = site = h->n_buckets; k = __hash_func(key); i = k & mask; \ + if (__ac_isempty(h->flags, i)) x = i; /* for speed up */ \ + else { \ + last = i; \ + while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \ + if (__ac_isdel(h->flags, i)) site = i; \ + i = (i + (++step)) & mask; \ + if (i == last) { x = site; break; } \ + } \ + if (x == h->n_buckets) { \ + if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \ + else x = i; \ + } \ + } \ + } \ + if (__ac_isempty(h->flags, x)) { /* not present at all */ \ + h->keys[x] = key; \ + __ac_set_isboth_false(h->flags, x); \ + ++h->size; ++h->n_occupied; \ + *ret = 1; \ + } else if (__ac_isdel(h->flags, x)) { /* deleted */ \ + h->keys[x] = key; \ + __ac_set_isboth_false(h->flags, x); \ + ++h->size; \ + *ret = 2; \ + } else *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \ + return x; \ + } \ + SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x) \ + { \ + if (x != h->n_buckets && !__ac_iseither(h->flags, x)) { \ + __ac_set_isdel_true(h->flags, x); \ + --h->size; \ + } \ + } + +#define KHASH_DECLARE(name, khkey_t, khval_t) \ + __KHASH_TYPE(name, khkey_t, khval_t) \ + __KHASH_PROTOTYPES(name, khkey_t, khval_t) + +#define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ + __KHASH_TYPE(name, khkey_t, khval_t) \ + __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) + +#define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ + KHASH_INIT2(name, static kh_inline klib_unused, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) + +/* --- BEGIN OF HASH FUNCTIONS --- */ + +/*! @function + @abstract Integer hash function + @param key The integer [khint32_t] + @return The hash value [khint_t] + */ +#define kh_int_hash_func(key) (khint32_t)(key) +/*! @function + @abstract Integer comparison function + */ +#define kh_int_hash_equal(a, b) ((a) == (b)) +/*! @function + @abstract 64-bit integer hash function + @param key The integer [khint64_t] + @return The hash value [khint_t] + */ +#define kh_int64_hash_func(key) (khint32_t)((key)>>33^(key)^(key)<<11) +/*! @function + @abstract 64-bit integer comparison function + */ +#define kh_int64_hash_equal(a, b) ((a) == (b)) +/*! @function + @abstract const char* hash function + @param s Pointer to a null terminated string + @return The hash value + */ +static kh_inline khint_t __ac_X31_hash_string(const char *s) +{ + khint_t h = (khint_t)*s; + if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)*s; + return h; +} +/*! @function + @abstract Another interface to const char* hash function + @param key Pointer to a null terminated string [const char*] + @return The hash value [khint_t] + */ +#define kh_str_hash_func(key) __ac_X31_hash_string(key) +/*! @function + @abstract Const char* comparison function + */ +#define kh_str_hash_equal(a, b) (strcmp(a, b) == 0) + +static kh_inline khint_t __ac_Wang_hash(khint_t key) +{ + key += ~(key << 15); + key ^= (key >> 10); + key += (key << 3); + key ^= (key >> 6); + key += ~(key << 11); + key ^= (key >> 16); + return key; +} +#define kh_int_hash_func2(key) __ac_Wang_hash((khint_t)key) + +/* --- END OF HASH FUNCTIONS --- */ + +/* Other convenient macros... */ + +/*! + @abstract Type of the hash table. + @param name Name of the hash table [symbol] + */ +#define khash_t(name) kh_##name##_t + +/*! @function + @abstract Initiate a hash table. + @param name Name of the hash table [symbol] + @return Pointer to the hash table [khash_t(name)*] + */ +#define kh_init(name) kh_init_##name() + +/*! @function + @abstract Destroy a hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + */ +#define kh_destroy(name, h) kh_destroy_##name(h) + +/*! @function + @abstract Reset a hash table without deallocating memory. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + */ +#define kh_clear(name, h) kh_clear_##name(h) + +/*! @function + @abstract Resize a hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param s New size [khint_t] + */ +#define kh_resize(name, h, s) kh_resize_##name(h, s) + +/*! @function + @abstract Insert a key to the hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param k Key [type of keys] + @param r Extra return code: -1 if the operation failed; + 0 if the key is present in the hash table; + 1 if the bucket is empty (never used); 2 if the element in + the bucket has been deleted [int*] + @return Iterator to the inserted element [khint_t] + */ +#define kh_put(name, h, k, r) kh_put_##name(h, k, r) + +/*! @function + @abstract Retrieve a key from the hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param k Key [type of keys] + @return Iterator to the found element, or kh_end(h) if the element is absent [khint_t] + */ +#define kh_get(name, h, k) kh_get_##name(h, k) + +/*! @function + @abstract Remove a key from the hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param k Iterator to the element to be deleted [khint_t] + */ +#define kh_del(name, h, k) kh_del_##name(h, k) + +/*! @function + @abstract Test whether a bucket contains data. + @param h Pointer to the hash table [khash_t(name)*] + @param x Iterator to the bucket [khint_t] + @return 1 if containing data; 0 otherwise [int] + */ +#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x))) + +/*! @function + @abstract Get key given an iterator + @param h Pointer to the hash table [khash_t(name)*] + @param x Iterator to the bucket [khint_t] + @return Key [type of keys] + */ +#define kh_key(h, x) ((h)->keys[x]) + +/*! @function + @abstract Get value given an iterator + @param h Pointer to the hash table [khash_t(name)*] + @param x Iterator to the bucket [khint_t] + @return Value [type of values] + @discussion For hash sets, calling this results in segfault. + */ +#define kh_val(h, x) ((h)->vals[x]) + +/*! @function + @abstract Alias of kh_val() + */ +#define kh_value(h, x) ((h)->vals[x]) + +/*! @function + @abstract Get the start iterator + @param h Pointer to the hash table [khash_t(name)*] + @return The start iterator [khint_t] + */ +#define kh_begin(h) (khint_t)(0) + +/*! @function + @abstract Get the end iterator + @param h Pointer to the hash table [khash_t(name)*] + @return The end iterator [khint_t] + */ +#define kh_end(h) ((h)->n_buckets) + +/*! @function + @abstract Get the number of elements in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @return Number of elements in the hash table [khint_t] + */ +#define kh_size(h) ((h)->size) + +/*! @function + @abstract Get the number of buckets in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @return Number of buckets in the hash table [khint_t] + */ +#define kh_n_buckets(h) ((h)->n_buckets) + +/*! @function + @abstract Iterate over the entries in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @param kvar Variable to which key will be assigned + @param vvar Variable to which value will be assigned + @param code Block of code to execute + */ +#define kh_foreach(h, kvar, vvar, code) { khint_t __i; \ + for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \ + if (!kh_exist(h,__i)) continue; \ + (kvar) = kh_key(h,__i); \ + (vvar) = kh_val(h,__i); \ + code; \ + } } + +/*! @function + @abstract Iterate over the values in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @param vvar Variable to which value will be assigned + @param code Block of code to execute + */ +#define kh_foreach_value(h, vvar, code) { khint_t __i; \ + for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \ + if (!kh_exist(h,__i)) continue; \ + (vvar) = kh_val(h,__i); \ + code; \ + } } + +/* More convenient interfaces */ + +/*! @function + @abstract Instantiate a hash set containing integer keys + @param name Name of the hash table [symbol] + */ +#define KHASH_SET_INIT_INT(name) \ + KHASH_INIT(name, khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing integer keys + @param name Name of the hash table [symbol] + @param khval_t Type of values [type] + */ +#define KHASH_MAP_INIT_INT(name, khval_t) \ + KHASH_INIT(name, khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal) + +/*! @function + @abstract Instantiate a hash set containing 64-bit integer keys + @param name Name of the hash table [symbol] + */ +#define KHASH_SET_INIT_INT64(name) \ + KHASH_INIT(name, khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing 64-bit integer keys + @param name Name of the hash table [symbol] + @param khval_t Type of values [type] + */ +#define KHASH_MAP_INIT_INT64(name, khval_t) \ + KHASH_INIT(name, khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal) + +typedef const char *kh_cstr_t; +/*! @function + @abstract Instantiate a hash map containing const char* keys + @param name Name of the hash table [symbol] + */ +#define KHASH_SET_INIT_STR(name) \ + KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing const char* keys + @param name Name of the hash table [symbol] + @param khval_t Type of values [type] + */ +#define KHASH_MAP_INIT_STR(name, khval_t) \ + KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal) + +#endif /* __AC_KHASH_H */ diff --git a/misc/benchmarks/external/skarupke/flat_hash_map.hpp b/misc/benchmarks/external/skarupke/flat_hash_map.hpp new file mode 100644 index 00000000..a8723ee8 --- /dev/null +++ b/misc/benchmarks/external/skarupke/flat_hash_map.hpp @@ -0,0 +1,1496 @@ +// Copyright Malte Skarupke 2017. +// Distributed under the Boost Software License, Version 1.0. +// (See http://www.boost.org/LICENSE_1_0.txt) + +#pragma once + +#include <cstdint> +#include <cstddef> +#include <functional> +#include <cmath> +#include <algorithm> +#include <iterator> +#include <utility> +#include <type_traits> + +#ifdef _MSC_VER +#define SKA_NOINLINE(...) __declspec(noinline) __VA_ARGS__ +#else +#define SKA_NOINLINE(...) __VA_ARGS__ __attribute__((noinline)) +#endif + +namespace ska +{ +struct prime_number_hash_policy; +struct power_of_two_hash_policy; +struct fibonacci_hash_policy; + +namespace detailv3 +{ +template<typename Result, typename Functor> +struct functor_storage : Functor +{ + functor_storage() = default; + functor_storage(const Functor & functor) + : Functor(functor) + { + } + template<typename... Args> + Result operator()(Args &&... args) + { + return static_cast<Functor &>(*this)(std::forward<Args>(args)...); + } + template<typename... Args> + Result operator()(Args &&... args) const + { + return static_cast<const Functor &>(*this)(std::forward<Args>(args)...); + } +}; +template<typename Result, typename... Args> +struct functor_storage<Result, Result (*)(Args...)> +{ + typedef Result (*function_ptr)(Args...); + function_ptr function; + functor_storage(function_ptr function) + : function(function) + { + } + Result operator()(Args... args) const + { + return function(std::forward<Args>(args)...); + } + operator function_ptr &() + { + return function; + } + operator const function_ptr &() + { + return function; + } +}; +template<typename key_type, typename value_type, typename hasher> +struct KeyOrValueHasher : functor_storage<size_t, hasher> +{ + typedef functor_storage<size_t, hasher> hasher_storage; + KeyOrValueHasher() = default; + KeyOrValueHasher(const hasher & hash) + : hasher_storage(hash) + { + } + size_t operator()(const key_type & key) + { + return static_cast<hasher_storage &>(*this)(key); + } + size_t operator()(const key_type & key) const + { + return static_cast<const hasher_storage &>(*this)(key); + } + size_t operator()(const value_type & value) + { + return static_cast<hasher_storage &>(*this)(value.first); + } + size_t operator()(const value_type & value) const + { + return static_cast<const hasher_storage &>(*this)(value.first); + } + template<typename F, typename S> + size_t operator()(const std::pair<F, S> & value) + { + return static_cast<hasher_storage &>(*this)(value.first); + } + template<typename F, typename S> + size_t operator()(const std::pair<F, S> & value) const + { + return static_cast<const hasher_storage &>(*this)(value.first); + } +}; +template<typename key_type, typename value_type, typename key_equal> +struct KeyOrValueEquality : functor_storage<bool, key_equal> +{ + typedef functor_storage<bool, key_equal> equality_storage; + KeyOrValueEquality() = default; + KeyOrValueEquality(const key_equal & equality) + : equality_storage(equality) + { + } + bool operator()(const key_type & lhs, const key_type & rhs) + { + return static_cast<equality_storage &>(*this)(lhs, rhs); + } + bool operator()(const key_type & lhs, const value_type & rhs) + { + return static_cast<equality_storage &>(*this)(lhs, rhs.first); + } + bool operator()(const value_type & lhs, const key_type & rhs) + { + return static_cast<equality_storage &>(*this)(lhs.first, rhs); + } + bool operator()(const value_type & lhs, const value_type & rhs) + { + return static_cast<equality_storage &>(*this)(lhs.first, rhs.first); + } + template<typename F, typename S> + bool operator()(const key_type & lhs, const std::pair<F, S> & rhs) + { + return static_cast<equality_storage &>(*this)(lhs, rhs.first); + } + template<typename F, typename S> + bool operator()(const std::pair<F, S> & lhs, const key_type & rhs) + { + return static_cast<equality_storage &>(*this)(lhs.first, rhs); + } + template<typename F, typename S> + bool operator()(const value_type & lhs, const std::pair<F, S> & rhs) + { + return static_cast<equality_storage &>(*this)(lhs.first, rhs.first); + } + template<typename F, typename S> + bool operator()(const std::pair<F, S> & lhs, const value_type & rhs) + { + return static_cast<equality_storage &>(*this)(lhs.first, rhs.first); + } + template<typename FL, typename SL, typename FR, typename SR> + bool operator()(const std::pair<FL, SL> & lhs, const std::pair<FR, SR> & rhs) + { + return static_cast<equality_storage &>(*this)(lhs.first, rhs.first); + } +}; +static constexpr int8_t min_lookups = 4; +template<typename T> +struct sherwood_v3_entry +{ + sherwood_v3_entry() + { + } + sherwood_v3_entry(int8_t distance_from_desired) + : distance_from_desired(distance_from_desired) + { + } + ~sherwood_v3_entry() + { + } + static sherwood_v3_entry * empty_default_table() + { + static sherwood_v3_entry result[min_lookups] = { {}, {}, {}, {special_end_value} }; + return result; + } + + bool has_value() const + { + return distance_from_desired >= 0; + } + bool is_empty() const + { + return distance_from_desired < 0; + } + bool is_at_desired_position() const + { + return distance_from_desired <= 0; + } + template<typename... Args> + void emplace(int8_t distance, Args &&... args) + { + new (std::addressof(value)) T(std::forward<Args>(args)...); + distance_from_desired = distance; + } + + void destroy_value() + { + value.~T(); + distance_from_desired = -1; + } + + int8_t distance_from_desired = -1; + static constexpr int8_t special_end_value = 0; + union { T value; }; +}; + +inline int8_t log2(size_t value) +{ + static constexpr int8_t table[64] = + { + 63, 0, 58, 1, 59, 47, 53, 2, + 60, 39, 48, 27, 54, 33, 42, 3, + 61, 51, 37, 40, 49, 18, 28, 20, + 55, 30, 34, 11, 43, 14, 22, 4, + 62, 57, 46, 52, 38, 26, 32, 41, + 50, 36, 17, 19, 29, 10, 13, 21, + 56, 45, 25, 31, 35, 16, 9, 12, + 44, 24, 15, 8, 23, 7, 6, 5 + }; + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + value |= value >> 32; + return table[((value - (value >> 1)) * 0x07EDD5E59A4E28C2) >> 58]; +} + +template<typename T, bool> +struct AssignIfTrue +{ + void operator()(T & lhs, const T & rhs) + { + lhs = rhs; + } + void operator()(T & lhs, T && rhs) + { + lhs = std::move(rhs); + } +}; +template<typename T> +struct AssignIfTrue<T, false> +{ + void operator()(T &, const T &) + { + } + void operator()(T &, T &&) + { + } +}; + +inline size_t next_power_of_two(size_t i) +{ + --i; + i |= i >> 1; + i |= i >> 2; + i |= i >> 4; + i |= i >> 8; + i |= i >> 16; + i |= i >> 32; + ++i; + return i; +} + +template<typename...> using void_t = void; + +template<typename T, typename = void> +struct HashPolicySelector +{ + typedef fibonacci_hash_policy type; +}; +template<typename T> +struct HashPolicySelector<T, void_t<typename T::hash_policy>> +{ + typedef typename T::hash_policy type; +}; + +template<typename T, typename FindKey, typename ArgumentHash, typename Hasher, typename ArgumentEqual, typename Equal, typename ArgumentAlloc, typename EntryAlloc> +class sherwood_v3_table : private EntryAlloc, private Hasher, private Equal +{ + using Entry = detailv3::sherwood_v3_entry<T>; + using AllocatorTraits = std::allocator_traits<EntryAlloc>; + using EntryPointer = typename AllocatorTraits::pointer; + struct convertible_to_iterator; + +public: + + using value_type = T; + using size_type = size_t; + using difference_type = std::ptrdiff_t; + using hasher = ArgumentHash; + using key_equal = ArgumentEqual; + using allocator_type = EntryAlloc; + using reference = value_type &; + using const_reference = const value_type &; + using pointer = value_type *; + using const_pointer = const value_type *; + + sherwood_v3_table() + { + } + explicit sherwood_v3_table(size_type bucket_count, const ArgumentHash & hash = ArgumentHash(), const ArgumentEqual & equal = ArgumentEqual(), const ArgumentAlloc & alloc = ArgumentAlloc()) + : EntryAlloc(alloc), Hasher(hash), Equal(equal) + { + rehash(bucket_count); + } + sherwood_v3_table(size_type bucket_count, const ArgumentAlloc & alloc) + : sherwood_v3_table(bucket_count, ArgumentHash(), ArgumentEqual(), alloc) + { + } + sherwood_v3_table(size_type bucket_count, const ArgumentHash & hash, const ArgumentAlloc & alloc) + : sherwood_v3_table(bucket_count, hash, ArgumentEqual(), alloc) + { + } + explicit sherwood_v3_table(const ArgumentAlloc & alloc) + : EntryAlloc(alloc) + { + } + template<typename It> + sherwood_v3_table(It first, It last, size_type bucket_count = 0, const ArgumentHash & hash = ArgumentHash(), const ArgumentEqual & equal = ArgumentEqual(), const ArgumentAlloc & alloc = ArgumentAlloc()) + : sherwood_v3_table(bucket_count, hash, equal, alloc) + { + insert(first, last); + } + template<typename It> + sherwood_v3_table(It first, It last, size_type bucket_count, const ArgumentAlloc & alloc) + : sherwood_v3_table(first, last, bucket_count, ArgumentHash(), ArgumentEqual(), alloc) + { + } + template<typename It> + sherwood_v3_table(It first, It last, size_type bucket_count, const ArgumentHash & hash, const ArgumentAlloc & alloc) + : sherwood_v3_table(first, last, bucket_count, hash, ArgumentEqual(), alloc) + { + } + sherwood_v3_table(std::initializer_list<T> il, size_type bucket_count = 0, const ArgumentHash & hash = ArgumentHash(), const ArgumentEqual & equal = ArgumentEqual(), const ArgumentAlloc & alloc = ArgumentAlloc()) + : sherwood_v3_table(bucket_count, hash, equal, alloc) + { + if (bucket_count == 0) + rehash(il.size()); + insert(il.begin(), il.end()); + } + sherwood_v3_table(std::initializer_list<T> il, size_type bucket_count, const ArgumentAlloc & alloc) + : sherwood_v3_table(il, bucket_count, ArgumentHash(), ArgumentEqual(), alloc) + { + } + sherwood_v3_table(std::initializer_list<T> il, size_type bucket_count, const ArgumentHash & hash, const ArgumentAlloc & alloc) + : sherwood_v3_table(il, bucket_count, hash, ArgumentEqual(), alloc) + { + } + sherwood_v3_table(const sherwood_v3_table & other) + : sherwood_v3_table(other, AllocatorTraits::select_on_container_copy_construction(other.get_allocator())) + { + } + sherwood_v3_table(const sherwood_v3_table & other, const ArgumentAlloc & alloc) + : EntryAlloc(alloc), Hasher(other), Equal(other), _max_load_factor(other._max_load_factor) + { + rehash_for_other_container(other); + try + { + insert(other.begin(), other.end()); + } + catch(...) + { + clear(); + deallocate_data(entries, num_slots_minus_one, max_lookups); + throw; + } + } + sherwood_v3_table(sherwood_v3_table && other) noexcept + : EntryAlloc(std::move(other)), Hasher(std::move(other)), Equal(std::move(other)) + { + swap_pointers(other); + } + sherwood_v3_table(sherwood_v3_table && other, const ArgumentAlloc & alloc) noexcept + : EntryAlloc(alloc), Hasher(std::move(other)), Equal(std::move(other)) + { + swap_pointers(other); + } + sherwood_v3_table & operator=(const sherwood_v3_table & other) + { + if (this == std::addressof(other)) + return *this; + + clear(); + if (AllocatorTraits::propagate_on_container_copy_assignment::value) + { + if (static_cast<EntryAlloc &>(*this) != static_cast<const EntryAlloc &>(other)) + { + reset_to_empty_state(); + } + AssignIfTrue<EntryAlloc, AllocatorTraits::propagate_on_container_copy_assignment::value>()(*this, other); + } + _max_load_factor = other._max_load_factor; + static_cast<Hasher &>(*this) = other; + static_cast<Equal &>(*this) = other; + rehash_for_other_container(other); + insert(other.begin(), other.end()); + return *this; + } + sherwood_v3_table & operator=(sherwood_v3_table && other) noexcept + { + if (this == std::addressof(other)) + return *this; + else if (AllocatorTraits::propagate_on_container_move_assignment::value) + { + clear(); + reset_to_empty_state(); + AssignIfTrue<EntryAlloc, AllocatorTraits::propagate_on_container_move_assignment::value>()(*this, std::move(other)); + swap_pointers(other); + } + else if (static_cast<EntryAlloc &>(*this) == static_cast<EntryAlloc &>(other)) + { + swap_pointers(other); + } + else + { + clear(); + _max_load_factor = other._max_load_factor; + rehash_for_other_container(other); + for (T & elem : other) + emplace(std::move(elem)); + other.clear(); + } + static_cast<Hasher &>(*this) = std::move(other); + static_cast<Equal &>(*this) = std::move(other); + return *this; + } + ~sherwood_v3_table() + { + clear(); + deallocate_data(entries, num_slots_minus_one, max_lookups); + } + + const allocator_type & get_allocator() const + { + return static_cast<const allocator_type &>(*this); + } + const ArgumentEqual & key_eq() const + { + return static_cast<const ArgumentEqual &>(*this); + } + const ArgumentHash & hash_function() const + { + return static_cast<const ArgumentHash &>(*this); + } + + template<typename ValueType> + struct templated_iterator + { + templated_iterator() = default; + templated_iterator(EntryPointer current) + : current(current) + { + } + EntryPointer current = EntryPointer(); + + using iterator_category = std::forward_iterator_tag; + using value_type = ValueType; + using difference_type = ptrdiff_t; + using pointer = ValueType *; + using reference = ValueType &; + + friend bool operator==(const templated_iterator & lhs, const templated_iterator & rhs) + { + return lhs.current == rhs.current; + } + friend bool operator!=(const templated_iterator & lhs, const templated_iterator & rhs) + { + return !(lhs == rhs); + } + + templated_iterator & operator++() + { + do + { + ++current; + } + while(current->is_empty()); + return *this; + } + templated_iterator operator++(int) + { + templated_iterator copy(*this); + ++*this; + return copy; + } + + ValueType & operator*() const + { + return current->value; + } + ValueType * operator->() const + { + return std::addressof(current->value); + } + + operator templated_iterator<const value_type>() const + { + return { current }; + } + }; + using iterator = templated_iterator<value_type>; + using const_iterator = templated_iterator<const value_type>; + + iterator begin() + { + for (EntryPointer it = entries;; ++it) + { + if (it->has_value()) + return { it }; + } + } + const_iterator begin() const + { + for (EntryPointer it = entries;; ++it) + { + if (it->has_value()) + return { it }; + } + } + const_iterator cbegin() const + { + return begin(); + } + iterator end() + { + return { entries + static_cast<ptrdiff_t>(num_slots_minus_one + max_lookups) }; + } + const_iterator end() const + { + return { entries + static_cast<ptrdiff_t>(num_slots_minus_one + max_lookups) }; + } + const_iterator cend() const + { + return end(); + } + + iterator find(const FindKey & key) + { + size_t index = hash_policy.index_for_hash(hash_object(key), num_slots_minus_one); + EntryPointer it = entries + ptrdiff_t(index); + for (int8_t distance = 0; it->distance_from_desired >= distance; ++distance, ++it) + { + if (compares_equal(key, it->value)) + return { it }; + } + return end(); + } + const_iterator find(const FindKey & key) const + { + return const_cast<sherwood_v3_table *>(this)->find(key); + } + size_t count(const FindKey & key) const + { + return find(key) == end() ? 0 : 1; + } + std::pair<iterator, iterator> equal_range(const FindKey & key) + { + iterator found = find(key); + if (found == end()) + return { found, found }; + else + return { found, std::next(found) }; + } + std::pair<const_iterator, const_iterator> equal_range(const FindKey & key) const + { + const_iterator found = find(key); + if (found == end()) + return { found, found }; + else + return { found, std::next(found) }; + } + + template<typename Key, typename... Args> + std::pair<iterator, bool> emplace(Key && key, Args &&... args) + { + size_t index = hash_policy.index_for_hash(hash_object(key), num_slots_minus_one); + EntryPointer current_entry = entries + ptrdiff_t(index); + int8_t distance_from_desired = 0; + for (; current_entry->distance_from_desired >= distance_from_desired; ++current_entry, ++distance_from_desired) + { + if (compares_equal(key, current_entry->value)) + return { { current_entry }, false }; + } + return emplace_new_key(distance_from_desired, current_entry, std::forward<Key>(key), std::forward<Args>(args)...); + } + + std::pair<iterator, bool> insert(const value_type & value) + { + return emplace(value); + } + std::pair<iterator, bool> insert(value_type && value) + { + return emplace(std::move(value)); + } + template<typename... Args> + iterator emplace_hint(const_iterator, Args &&... args) + { + return emplace(std::forward<Args>(args)...).first; + } + iterator insert(const_iterator, const value_type & value) + { + return emplace(value).first; + } + iterator insert(const_iterator, value_type && value) + { + return emplace(std::move(value)).first; + } + + template<typename It> + void insert(It begin, It end) + { + for (; begin != end; ++begin) + { + emplace(*begin); + } + } + void insert(std::initializer_list<value_type> il) + { + insert(il.begin(), il.end()); + } + + void rehash(size_t num_buckets) + { + num_buckets = std::max(num_buckets, static_cast<size_t>(std::ceil(num_elements / static_cast<double>(_max_load_factor)))); + if (num_buckets == 0) + { + reset_to_empty_state(); + return; + } + auto new_prime_index = hash_policy.next_size_over(num_buckets); + if (num_buckets == bucket_count()) + return; + int8_t new_max_lookups = compute_max_lookups(num_buckets); + EntryPointer new_buckets(AllocatorTraits::allocate(*this, num_buckets + new_max_lookups)); + EntryPointer special_end_item = new_buckets + static_cast<ptrdiff_t>(num_buckets + new_max_lookups - 1); + for (EntryPointer it = new_buckets; it != special_end_item; ++it) + it->distance_from_desired = -1; + special_end_item->distance_from_desired = Entry::special_end_value; + std::swap(entries, new_buckets); + std::swap(num_slots_minus_one, num_buckets); + --num_slots_minus_one; + hash_policy.commit(new_prime_index); + int8_t old_max_lookups = max_lookups; + max_lookups = new_max_lookups; + num_elements = 0; + for (EntryPointer it = new_buckets, end = it + static_cast<ptrdiff_t>(num_buckets + old_max_lookups); it != end; ++it) + { + if (it->has_value()) + { + emplace(std::move(it->value)); + it->destroy_value(); + } + } + deallocate_data(new_buckets, num_buckets, old_max_lookups); + } + + void reserve(size_t num_elements) + { + size_t required_buckets = num_buckets_for_reserve(num_elements); + if (required_buckets > bucket_count()) + rehash(required_buckets); + } + + // the return value is a type that can be converted to an iterator + // the reason for doing this is that it's not free to find the + // iterator pointing at the next element. if you care about the + // next iterator, turn the return value into an iterator + convertible_to_iterator erase(const_iterator to_erase) + { + EntryPointer current = to_erase.current; + current->destroy_value(); + --num_elements; + for (EntryPointer next = current + ptrdiff_t(1); !next->is_at_desired_position(); ++current, ++next) + { + current->emplace(next->distance_from_desired - 1, std::move(next->value)); + next->destroy_value(); + } + return { to_erase.current }; + } + + iterator erase(const_iterator begin_it, const_iterator end_it) + { + if (begin_it == end_it) + return { begin_it.current }; + for (EntryPointer it = begin_it.current, end = end_it.current; it != end; ++it) + { + if (it->has_value()) + { + it->destroy_value(); + --num_elements; + } + } + if (end_it == this->end()) + return this->end(); + ptrdiff_t num_to_move = std::min(static_cast<ptrdiff_t>(end_it.current->distance_from_desired), end_it.current - begin_it.current); + EntryPointer to_return = end_it.current - num_to_move; + for (EntryPointer it = end_it.current; !it->is_at_desired_position();) + { + EntryPointer target = it - num_to_move; + target->emplace(it->distance_from_desired - num_to_move, std::move(it->value)); + it->destroy_value(); + ++it; + num_to_move = std::min(static_cast<ptrdiff_t>(it->distance_from_desired), num_to_move); + } + return { to_return }; + } + + size_t erase(const FindKey & key) + { + auto found = find(key); + if (found == end()) + return 0; + else + { + erase(found); + return 1; + } + } + + void clear() + { + for (EntryPointer it = entries, end = it + static_cast<ptrdiff_t>(num_slots_minus_one + max_lookups); it != end; ++it) + { + if (it->has_value()) + it->destroy_value(); + } + num_elements = 0; + } + + void shrink_to_fit() + { + rehash_for_other_container(*this); + } + + void swap(sherwood_v3_table & other) + { + using std::swap; + swap_pointers(other); + swap(static_cast<ArgumentHash &>(*this), static_cast<ArgumentHash &>(other)); + swap(static_cast<ArgumentEqual &>(*this), static_cast<ArgumentEqual &>(other)); + if (AllocatorTraits::propagate_on_container_swap::value) + swap(static_cast<EntryAlloc &>(*this), static_cast<EntryAlloc &>(other)); + } + + size_t size() const + { + return num_elements; + } + size_t max_size() const + { + return (AllocatorTraits::max_size(*this)) / sizeof(Entry); + } + size_t bucket_count() const + { + return num_slots_minus_one ? num_slots_minus_one + 1 : 0; + } + size_type max_bucket_count() const + { + return (AllocatorTraits::max_size(*this) - min_lookups) / sizeof(Entry); + } + size_t bucket(const FindKey & key) const + { + return hash_policy.index_for_hash(hash_object(key), num_slots_minus_one); + } + float load_factor() const + { + size_t buckets = bucket_count(); + if (buckets) + return static_cast<float>(num_elements) / bucket_count(); + else + return 0; + } + void max_load_factor(float value) + { + _max_load_factor = value; + } + float max_load_factor() const + { + return _max_load_factor; + } + + bool empty() const + { + return num_elements == 0; + } + +private: + EntryPointer entries = Entry::empty_default_table(); + size_t num_slots_minus_one = 0; + typename HashPolicySelector<ArgumentHash>::type hash_policy; + int8_t max_lookups = detailv3::min_lookups - 1; + float _max_load_factor = 0.5f; + size_t num_elements = 0; + + static int8_t compute_max_lookups(size_t num_buckets) + { + int8_t desired = detailv3::log2(num_buckets); + return std::max(detailv3::min_lookups, desired); + } + + size_t num_buckets_for_reserve(size_t num_elements) const + { + return static_cast<size_t>(std::ceil(num_elements / std::min(0.5, static_cast<double>(_max_load_factor)))); + } + void rehash_for_other_container(const sherwood_v3_table & other) + { + rehash(std::min(num_buckets_for_reserve(other.size()), other.bucket_count())); + } + + void swap_pointers(sherwood_v3_table & other) + { + using std::swap; + swap(hash_policy, other.hash_policy); + swap(entries, other.entries); + swap(num_slots_minus_one, other.num_slots_minus_one); + swap(num_elements, other.num_elements); + swap(max_lookups, other.max_lookups); + swap(_max_load_factor, other._max_load_factor); + } + + template<typename Key, typename... Args> + SKA_NOINLINE(std::pair<iterator, bool>) emplace_new_key(int8_t distance_from_desired, EntryPointer current_entry, Key && key, Args &&... args) + { + using std::swap; + if (num_slots_minus_one == 0 || distance_from_desired == max_lookups || num_elements + 1 > (num_slots_minus_one + 1) * static_cast<double>(_max_load_factor)) + { + grow(); + return emplace(std::forward<Key>(key), std::forward<Args>(args)...); + } + else if (current_entry->is_empty()) + { + current_entry->emplace(distance_from_desired, std::forward<Key>(key), std::forward<Args>(args)...); + ++num_elements; + return { { current_entry }, true }; + } + value_type to_insert(std::forward<Key>(key), std::forward<Args>(args)...); + swap(distance_from_desired, current_entry->distance_from_desired); + swap(to_insert, current_entry->value); + iterator result = { current_entry }; + for (++distance_from_desired, ++current_entry;; ++current_entry) + { + if (current_entry->is_empty()) + { + current_entry->emplace(distance_from_desired, std::move(to_insert)); + ++num_elements; + return { result, true }; + } + else if (current_entry->distance_from_desired < distance_from_desired) + { + swap(distance_from_desired, current_entry->distance_from_desired); + swap(to_insert, current_entry->value); + ++distance_from_desired; + } + else + { + ++distance_from_desired; + if (distance_from_desired == max_lookups) + { + swap(to_insert, result.current->value); + grow(); + return emplace(std::move(to_insert)); + } + } + } + } + + void grow() + { + rehash(std::max(size_t(4), 2 * bucket_count())); + } + + void deallocate_data(EntryPointer begin, size_t num_slots_minus_one, int8_t max_lookups) + { + if (begin != Entry::empty_default_table()) + { + AllocatorTraits::deallocate(*this, begin, num_slots_minus_one + max_lookups + 1); + } + } + + void reset_to_empty_state() + { + deallocate_data(entries, num_slots_minus_one, max_lookups); + entries = Entry::empty_default_table(); + num_slots_minus_one = 0; + hash_policy.reset(); + max_lookups = detailv3::min_lookups - 1; + } + + template<typename U> + size_t hash_object(const U & key) + { + return static_cast<Hasher &>(*this)(key); + } + template<typename U> + size_t hash_object(const U & key) const + { + return static_cast<const Hasher &>(*this)(key); + } + template<typename L, typename R> + bool compares_equal(const L & lhs, const R & rhs) + { + return static_cast<Equal &>(*this)(lhs, rhs); + } + + struct convertible_to_iterator + { + EntryPointer it; + + operator iterator() + { + if (it->has_value()) + return { it }; + else + return ++iterator{it}; + } + operator const_iterator() + { + if (it->has_value()) + return { it }; + else + return ++const_iterator{it}; + } + }; + +}; +} + +struct prime_number_hash_policy +{ + static size_t mod0(size_t) { return 0llu; } + static size_t mod2(size_t hash) { return hash % 2llu; } + static size_t mod3(size_t hash) { return hash % 3llu; } + static size_t mod5(size_t hash) { return hash % 5llu; } + static size_t mod7(size_t hash) { return hash % 7llu; } + static size_t mod11(size_t hash) { return hash % 11llu; } + static size_t mod13(size_t hash) { return hash % 13llu; } + static size_t mod17(size_t hash) { return hash % 17llu; } + static size_t mod23(size_t hash) { return hash % 23llu; } + static size_t mod29(size_t hash) { return hash % 29llu; } + static size_t mod37(size_t hash) { return hash % 37llu; } + static size_t mod47(size_t hash) { return hash % 47llu; } + static size_t mod59(size_t hash) { return hash % 59llu; } + static size_t mod73(size_t hash) { return hash % 73llu; } + static size_t mod97(size_t hash) { return hash % 97llu; } + static size_t mod127(size_t hash) { return hash % 127llu; } + static size_t mod151(size_t hash) { return hash % 151llu; } + static size_t mod197(size_t hash) { return hash % 197llu; } + static size_t mod251(size_t hash) { return hash % 251llu; } + static size_t mod313(size_t hash) { return hash % 313llu; } + static size_t mod397(size_t hash) { return hash % 397llu; } + static size_t mod499(size_t hash) { return hash % 499llu; } + static size_t mod631(size_t hash) { return hash % 631llu; } + static size_t mod797(size_t hash) { return hash % 797llu; } + static size_t mod1009(size_t hash) { return hash % 1009llu; } + static size_t mod1259(size_t hash) { return hash % 1259llu; } + static size_t mod1597(size_t hash) { return hash % 1597llu; } + static size_t mod2011(size_t hash) { return hash % 2011llu; } + static size_t mod2539(size_t hash) { return hash % 2539llu; } + static size_t mod3203(size_t hash) { return hash % 3203llu; } + static size_t mod4027(size_t hash) { return hash % 4027llu; } + static size_t mod5087(size_t hash) { return hash % 5087llu; } + static size_t mod6421(size_t hash) { return hash % 6421llu; } + static size_t mod8089(size_t hash) { return hash % 8089llu; } + static size_t mod10193(size_t hash) { return hash % 10193llu; } + static size_t mod12853(size_t hash) { return hash % 12853llu; } + static size_t mod16193(size_t hash) { return hash % 16193llu; } + static size_t mod20399(size_t hash) { return hash % 20399llu; } + static size_t mod25717(size_t hash) { return hash % 25717llu; } + static size_t mod32401(size_t hash) { return hash % 32401llu; } + static size_t mod40823(size_t hash) { return hash % 40823llu; } + static size_t mod51437(size_t hash) { return hash % 51437llu; } + static size_t mod64811(size_t hash) { return hash % 64811llu; } + static size_t mod81649(size_t hash) { return hash % 81649llu; } + static size_t mod102877(size_t hash) { return hash % 102877llu; } + static size_t mod129607(size_t hash) { return hash % 129607llu; } + static size_t mod163307(size_t hash) { return hash % 163307llu; } + static size_t mod205759(size_t hash) { return hash % 205759llu; } + static size_t mod259229(size_t hash) { return hash % 259229llu; } + static size_t mod326617(size_t hash) { return hash % 326617llu; } + static size_t mod411527(size_t hash) { return hash % 411527llu; } + static size_t mod518509(size_t hash) { return hash % 518509llu; } + static size_t mod653267(size_t hash) { return hash % 653267llu; } + static size_t mod823117(size_t hash) { return hash % 823117llu; } + static size_t mod1037059(size_t hash) { return hash % 1037059llu; } + static size_t mod1306601(size_t hash) { return hash % 1306601llu; } + static size_t mod1646237(size_t hash) { return hash % 1646237llu; } + static size_t mod2074129(size_t hash) { return hash % 2074129llu; } + static size_t mod2613229(size_t hash) { return hash % 2613229llu; } + static size_t mod3292489(size_t hash) { return hash % 3292489llu; } + static size_t mod4148279(size_t hash) { return hash % 4148279llu; } + static size_t mod5226491(size_t hash) { return hash % 5226491llu; } + static size_t mod6584983(size_t hash) { return hash % 6584983llu; } + static size_t mod8296553(size_t hash) { return hash % 8296553llu; } + static size_t mod10453007(size_t hash) { return hash % 10453007llu; } + static size_t mod13169977(size_t hash) { return hash % 13169977llu; } + static size_t mod16593127(size_t hash) { return hash % 16593127llu; } + static size_t mod20906033(size_t hash) { return hash % 20906033llu; } + static size_t mod26339969(size_t hash) { return hash % 26339969llu; } + static size_t mod33186281(size_t hash) { return hash % 33186281llu; } + static size_t mod41812097(size_t hash) { return hash % 41812097llu; } + static size_t mod52679969(size_t hash) { return hash % 52679969llu; } + static size_t mod66372617(size_t hash) { return hash % 66372617llu; } + static size_t mod83624237(size_t hash) { return hash % 83624237llu; } + static size_t mod105359939(size_t hash) { return hash % 105359939llu; } + static size_t mod132745199(size_t hash) { return hash % 132745199llu; } + static size_t mod167248483(size_t hash) { return hash % 167248483llu; } + static size_t mod210719881(size_t hash) { return hash % 210719881llu; } + static size_t mod265490441(size_t hash) { return hash % 265490441llu; } + static size_t mod334496971(size_t hash) { return hash % 334496971llu; } + static size_t mod421439783(size_t hash) { return hash % 421439783llu; } + static size_t mod530980861(size_t hash) { return hash % 530980861llu; } + static size_t mod668993977(size_t hash) { return hash % 668993977llu; } + static size_t mod842879579(size_t hash) { return hash % 842879579llu; } + static size_t mod1061961721(size_t hash) { return hash % 1061961721llu; } + static size_t mod1337987929(size_t hash) { return hash % 1337987929llu; } + static size_t mod1685759167(size_t hash) { return hash % 1685759167llu; } + static size_t mod2123923447(size_t hash) { return hash % 2123923447llu; } + static size_t mod2675975881(size_t hash) { return hash % 2675975881llu; } + static size_t mod3371518343(size_t hash) { return hash % 3371518343llu; } + static size_t mod4247846927(size_t hash) { return hash % 4247846927llu; } + static size_t mod5351951779(size_t hash) { return hash % 5351951779llu; } + static size_t mod6743036717(size_t hash) { return hash % 6743036717llu; } + static size_t mod8495693897(size_t hash) { return hash % 8495693897llu; } + static size_t mod10703903591(size_t hash) { return hash % 10703903591llu; } + static size_t mod13486073473(size_t hash) { return hash % 13486073473llu; } + static size_t mod16991387857(size_t hash) { return hash % 16991387857llu; } + static size_t mod21407807219(size_t hash) { return hash % 21407807219llu; } + static size_t mod26972146961(size_t hash) { return hash % 26972146961llu; } + static size_t mod33982775741(size_t hash) { return hash % 33982775741llu; } + static size_t mod42815614441(size_t hash) { return hash % 42815614441llu; } + static size_t mod53944293929(size_t hash) { return hash % 53944293929llu; } + static size_t mod67965551447(size_t hash) { return hash % 67965551447llu; } + static size_t mod85631228929(size_t hash) { return hash % 85631228929llu; } + static size_t mod107888587883(size_t hash) { return hash % 107888587883llu; } + static size_t mod135931102921(size_t hash) { return hash % 135931102921llu; } + static size_t mod171262457903(size_t hash) { return hash % 171262457903llu; } + static size_t mod215777175787(size_t hash) { return hash % 215777175787llu; } + static size_t mod271862205833(size_t hash) { return hash % 271862205833llu; } + static size_t mod342524915839(size_t hash) { return hash % 342524915839llu; } + static size_t mod431554351609(size_t hash) { return hash % 431554351609llu; } + static size_t mod543724411781(size_t hash) { return hash % 543724411781llu; } + static size_t mod685049831731(size_t hash) { return hash % 685049831731llu; } + static size_t mod863108703229(size_t hash) { return hash % 863108703229llu; } + static size_t mod1087448823553(size_t hash) { return hash % 1087448823553llu; } + static size_t mod1370099663459(size_t hash) { return hash % 1370099663459llu; } + static size_t mod1726217406467(size_t hash) { return hash % 1726217406467llu; } + static size_t mod2174897647073(size_t hash) { return hash % 2174897647073llu; } + static size_t mod2740199326961(size_t hash) { return hash % 2740199326961llu; } + static size_t mod3452434812973(size_t hash) { return hash % 3452434812973llu; } + static size_t mod4349795294267(size_t hash) { return hash % 4349795294267llu; } + static size_t mod5480398654009(size_t hash) { return hash % 5480398654009llu; } + static size_t mod6904869625999(size_t hash) { return hash % 6904869625999llu; } + static size_t mod8699590588571(size_t hash) { return hash % 8699590588571llu; } + static size_t mod10960797308051(size_t hash) { return hash % 10960797308051llu; } + static size_t mod13809739252051(size_t hash) { return hash % 13809739252051llu; } + static size_t mod17399181177241(size_t hash) { return hash % 17399181177241llu; } + static size_t mod21921594616111(size_t hash) { return hash % 21921594616111llu; } + static size_t mod27619478504183(size_t hash) { return hash % 27619478504183llu; } + static size_t mod34798362354533(size_t hash) { return hash % 34798362354533llu; } + static size_t mod43843189232363(size_t hash) { return hash % 43843189232363llu; } + static size_t mod55238957008387(size_t hash) { return hash % 55238957008387llu; } + static size_t mod69596724709081(size_t hash) { return hash % 69596724709081llu; } + static size_t mod87686378464759(size_t hash) { return hash % 87686378464759llu; } + static size_t mod110477914016779(size_t hash) { return hash % 110477914016779llu; } + static size_t mod139193449418173(size_t hash) { return hash % 139193449418173llu; } + static size_t mod175372756929481(size_t hash) { return hash % 175372756929481llu; } + static size_t mod220955828033581(size_t hash) { return hash % 220955828033581llu; } + static size_t mod278386898836457(size_t hash) { return hash % 278386898836457llu; } + static size_t mod350745513859007(size_t hash) { return hash % 350745513859007llu; } + static size_t mod441911656067171(size_t hash) { return hash % 441911656067171llu; } + static size_t mod556773797672909(size_t hash) { return hash % 556773797672909llu; } + static size_t mod701491027718027(size_t hash) { return hash % 701491027718027llu; } + static size_t mod883823312134381(size_t hash) { return hash % 883823312134381llu; } + static size_t mod1113547595345903(size_t hash) { return hash % 1113547595345903llu; } + static size_t mod1402982055436147(size_t hash) { return hash % 1402982055436147llu; } + static size_t mod1767646624268779(size_t hash) { return hash % 1767646624268779llu; } + static size_t mod2227095190691797(size_t hash) { return hash % 2227095190691797llu; } + static size_t mod2805964110872297(size_t hash) { return hash % 2805964110872297llu; } + static size_t mod3535293248537579(size_t hash) { return hash % 3535293248537579llu; } + static size_t mod4454190381383713(size_t hash) { return hash % 4454190381383713llu; } + static size_t mod5611928221744609(size_t hash) { return hash % 5611928221744609llu; } + static size_t mod7070586497075177(size_t hash) { return hash % 7070586497075177llu; } + static size_t mod8908380762767489(size_t hash) { return hash % 8908380762767489llu; } + static size_t mod11223856443489329(size_t hash) { return hash % 11223856443489329llu; } + static size_t mod14141172994150357(size_t hash) { return hash % 14141172994150357llu; } + static size_t mod17816761525534927(size_t hash) { return hash % 17816761525534927llu; } + static size_t mod22447712886978529(size_t hash) { return hash % 22447712886978529llu; } + static size_t mod28282345988300791(size_t hash) { return hash % 28282345988300791llu; } + static size_t mod35633523051069991(size_t hash) { return hash % 35633523051069991llu; } + static size_t mod44895425773957261(size_t hash) { return hash % 44895425773957261llu; } + static size_t mod56564691976601587(size_t hash) { return hash % 56564691976601587llu; } + static size_t mod71267046102139967(size_t hash) { return hash % 71267046102139967llu; } + static size_t mod89790851547914507(size_t hash) { return hash % 89790851547914507llu; } + static size_t mod113129383953203213(size_t hash) { return hash % 113129383953203213llu; } + static size_t mod142534092204280003(size_t hash) { return hash % 142534092204280003llu; } + static size_t mod179581703095829107(size_t hash) { return hash % 179581703095829107llu; } + static size_t mod226258767906406483(size_t hash) { return hash % 226258767906406483llu; } + static size_t mod285068184408560057(size_t hash) { return hash % 285068184408560057llu; } + static size_t mod359163406191658253(size_t hash) { return hash % 359163406191658253llu; } + static size_t mod452517535812813007(size_t hash) { return hash % 452517535812813007llu; } + static size_t mod570136368817120201(size_t hash) { return hash % 570136368817120201llu; } + static size_t mod718326812383316683(size_t hash) { return hash % 718326812383316683llu; } + static size_t mod905035071625626043(size_t hash) { return hash % 905035071625626043llu; } + static size_t mod1140272737634240411(size_t hash) { return hash % 1140272737634240411llu; } + static size_t mod1436653624766633509(size_t hash) { return hash % 1436653624766633509llu; } + static size_t mod1810070143251252131(size_t hash) { return hash % 1810070143251252131llu; } + static size_t mod2280545475268481167(size_t hash) { return hash % 2280545475268481167llu; } + static size_t mod2873307249533267101(size_t hash) { return hash % 2873307249533267101llu; } + static size_t mod3620140286502504283(size_t hash) { return hash % 3620140286502504283llu; } + static size_t mod4561090950536962147(size_t hash) { return hash % 4561090950536962147llu; } + static size_t mod5746614499066534157(size_t hash) { return hash % 5746614499066534157llu; } + static size_t mod7240280573005008577(size_t hash) { return hash % 7240280573005008577llu; } + static size_t mod9122181901073924329(size_t hash) { return hash % 9122181901073924329llu; } + static size_t mod11493228998133068689(size_t hash) { return hash % 11493228998133068689llu; } + static size_t mod14480561146010017169(size_t hash) { return hash % 14480561146010017169llu; } + static size_t mod18446744073709551557(size_t hash) { return hash % 18446744073709551557llu; } + + using mod_function = size_t (*)(size_t); + + mod_function next_size_over(size_t & size) const + { + // prime numbers generated by the following method: + // 1. start with a prime p = 2 + // 2. go to wolfram alpha and get p = NextPrime(2 * p) + // 3. repeat 2. until you overflow 64 bits + // you now have large gaps which you would hit if somebody called reserve() with an unlucky number. + // 4. to fill the gaps for every prime p go to wolfram alpha and get ClosestPrime(p * 2^(1/3)) and ClosestPrime(p * 2^(2/3)) and put those in the gaps + // 5. get PrevPrime(2^64) and put it at the end + static constexpr const size_t prime_list[] = + { + 2llu, 3llu, 5llu, 7llu, 11llu, 13llu, 17llu, 23llu, 29llu, 37llu, 47llu, + 59llu, 73llu, 97llu, 127llu, 151llu, 197llu, 251llu, 313llu, 397llu, + 499llu, 631llu, 797llu, 1009llu, 1259llu, 1597llu, 2011llu, 2539llu, + 3203llu, 4027llu, 5087llu, 6421llu, 8089llu, 10193llu, 12853llu, 16193llu, + 20399llu, 25717llu, 32401llu, 40823llu, 51437llu, 64811llu, 81649llu, + 102877llu, 129607llu, 163307llu, 205759llu, 259229llu, 326617llu, + 411527llu, 518509llu, 653267llu, 823117llu, 1037059llu, 1306601llu, + 1646237llu, 2074129llu, 2613229llu, 3292489llu, 4148279llu, 5226491llu, + 6584983llu, 8296553llu, 10453007llu, 13169977llu, 16593127llu, 20906033llu, + 26339969llu, 33186281llu, 41812097llu, 52679969llu, 66372617llu, + 83624237llu, 105359939llu, 132745199llu, 167248483llu, 210719881llu, + 265490441llu, 334496971llu, 421439783llu, 530980861llu, 668993977llu, + 842879579llu, 1061961721llu, 1337987929llu, 1685759167llu, 2123923447llu, + 2675975881llu, 3371518343llu, 4247846927llu, 5351951779llu, 6743036717llu, + 8495693897llu, 10703903591llu, 13486073473llu, 16991387857llu, + 21407807219llu, 26972146961llu, 33982775741llu, 42815614441llu, + 53944293929llu, 67965551447llu, 85631228929llu, 107888587883llu, + 135931102921llu, 171262457903llu, 215777175787llu, 271862205833llu, + 342524915839llu, 431554351609llu, 543724411781llu, 685049831731llu, + 863108703229llu, 1087448823553llu, 1370099663459llu, 1726217406467llu, + 2174897647073llu, 2740199326961llu, 3452434812973llu, 4349795294267llu, + 5480398654009llu, 6904869625999llu, 8699590588571llu, 10960797308051llu, + 13809739252051llu, 17399181177241llu, 21921594616111llu, 27619478504183llu, + 34798362354533llu, 43843189232363llu, 55238957008387llu, 69596724709081llu, + 87686378464759llu, 110477914016779llu, 139193449418173llu, + 175372756929481llu, 220955828033581llu, 278386898836457llu, + 350745513859007llu, 441911656067171llu, 556773797672909llu, + 701491027718027llu, 883823312134381llu, 1113547595345903llu, + 1402982055436147llu, 1767646624268779llu, 2227095190691797llu, + 2805964110872297llu, 3535293248537579llu, 4454190381383713llu, + 5611928221744609llu, 7070586497075177llu, 8908380762767489llu, + 11223856443489329llu, 14141172994150357llu, 17816761525534927llu, + 22447712886978529llu, 28282345988300791llu, 35633523051069991llu, + 44895425773957261llu, 56564691976601587llu, 71267046102139967llu, + 89790851547914507llu, 113129383953203213llu, 142534092204280003llu, + 179581703095829107llu, 226258767906406483llu, 285068184408560057llu, + 359163406191658253llu, 452517535812813007llu, 570136368817120201llu, + 718326812383316683llu, 905035071625626043llu, 1140272737634240411llu, + 1436653624766633509llu, 1810070143251252131llu, 2280545475268481167llu, + 2873307249533267101llu, 3620140286502504283llu, 4561090950536962147llu, + 5746614499066534157llu, 7240280573005008577llu, 9122181901073924329llu, + 11493228998133068689llu, 14480561146010017169llu, 18446744073709551557llu + }; + static constexpr size_t (* const mod_functions[])(size_t) = + { + &mod0, &mod2, &mod3, &mod5, &mod7, &mod11, &mod13, &mod17, &mod23, &mod29, &mod37, + &mod47, &mod59, &mod73, &mod97, &mod127, &mod151, &mod197, &mod251, &mod313, &mod397, + &mod499, &mod631, &mod797, &mod1009, &mod1259, &mod1597, &mod2011, &mod2539, &mod3203, + &mod4027, &mod5087, &mod6421, &mod8089, &mod10193, &mod12853, &mod16193, &mod20399, + &mod25717, &mod32401, &mod40823, &mod51437, &mod64811, &mod81649, &mod102877, + &mod129607, &mod163307, &mod205759, &mod259229, &mod326617, &mod411527, &mod518509, + &mod653267, &mod823117, &mod1037059, &mod1306601, &mod1646237, &mod2074129, + &mod2613229, &mod3292489, &mod4148279, &mod5226491, &mod6584983, &mod8296553, + &mod10453007, &mod13169977, &mod16593127, &mod20906033, &mod26339969, &mod33186281, + &mod41812097, &mod52679969, &mod66372617, &mod83624237, &mod105359939, &mod132745199, + &mod167248483, &mod210719881, &mod265490441, &mod334496971, &mod421439783, + &mod530980861, &mod668993977, &mod842879579, &mod1061961721, &mod1337987929, + &mod1685759167, &mod2123923447, &mod2675975881, &mod3371518343, &mod4247846927, + &mod5351951779, &mod6743036717, &mod8495693897, &mod10703903591, &mod13486073473, + &mod16991387857, &mod21407807219, &mod26972146961, &mod33982775741, &mod42815614441, + &mod53944293929, &mod67965551447, &mod85631228929, &mod107888587883, &mod135931102921, + &mod171262457903, &mod215777175787, &mod271862205833, &mod342524915839, + &mod431554351609, &mod543724411781, &mod685049831731, &mod863108703229, + &mod1087448823553, &mod1370099663459, &mod1726217406467, &mod2174897647073, + &mod2740199326961, &mod3452434812973, &mod4349795294267, &mod5480398654009, + &mod6904869625999, &mod8699590588571, &mod10960797308051, &mod13809739252051, + &mod17399181177241, &mod21921594616111, &mod27619478504183, &mod34798362354533, + &mod43843189232363, &mod55238957008387, &mod69596724709081, &mod87686378464759, + &mod110477914016779, &mod139193449418173, &mod175372756929481, &mod220955828033581, + &mod278386898836457, &mod350745513859007, &mod441911656067171, &mod556773797672909, + &mod701491027718027, &mod883823312134381, &mod1113547595345903, &mod1402982055436147, + &mod1767646624268779, &mod2227095190691797, &mod2805964110872297, &mod3535293248537579, + &mod4454190381383713, &mod5611928221744609, &mod7070586497075177, &mod8908380762767489, + &mod11223856443489329, &mod14141172994150357, &mod17816761525534927, + &mod22447712886978529, &mod28282345988300791, &mod35633523051069991, + &mod44895425773957261, &mod56564691976601587, &mod71267046102139967, + &mod89790851547914507, &mod113129383953203213, &mod142534092204280003, + &mod179581703095829107, &mod226258767906406483, &mod285068184408560057, + &mod359163406191658253, &mod452517535812813007, &mod570136368817120201, + &mod718326812383316683, &mod905035071625626043, &mod1140272737634240411, + &mod1436653624766633509, &mod1810070143251252131, &mod2280545475268481167, + &mod2873307249533267101, &mod3620140286502504283, &mod4561090950536962147, + &mod5746614499066534157, &mod7240280573005008577, &mod9122181901073924329, + &mod11493228998133068689, &mod14480561146010017169, &mod18446744073709551557 + }; + const size_t * found = std::lower_bound(std::begin(prime_list), std::end(prime_list) - 1, size); + size = *found; + return mod_functions[1 + found - prime_list]; + } + void commit(mod_function new_mod_function) + { + current_mod_function = new_mod_function; + } + void reset() + { + current_mod_function = &mod0; + } + + size_t index_for_hash(size_t hash, size_t /*num_slots_minus_one*/) const + { + return current_mod_function(hash); + } + size_t keep_in_range(size_t index, size_t num_slots_minus_one) const + { + return index > num_slots_minus_one ? current_mod_function(index) : index; + } + +private: + mod_function current_mod_function = &mod0; +}; + +struct power_of_two_hash_policy +{ + size_t index_for_hash(size_t hash, size_t num_slots_minus_one) const + { + return hash & num_slots_minus_one; + } + size_t keep_in_range(size_t index, size_t num_slots_minus_one) const + { + return index_for_hash(index, num_slots_minus_one); + } + int8_t next_size_over(size_t & size) const + { + size = detailv3::next_power_of_two(size); + return 0; + } + void commit(int8_t) + { + } + void reset() + { + } + +}; + +struct fibonacci_hash_policy +{ + size_t index_for_hash(size_t hash, size_t /*num_slots_minus_one*/) const + { + return (11400714819323198485ull * hash) >> shift; + } + size_t keep_in_range(size_t index, size_t num_slots_minus_one) const + { + return index & num_slots_minus_one; + } + + int8_t next_size_over(size_t & size) const + { + size = std::max(size_t(2), detailv3::next_power_of_two(size)); + return 64 - detailv3::log2(size); + } + void commit(int8_t shift) + { + this->shift = shift; + } + void reset() + { + shift = 63; + } + +private: + int8_t shift = 63; +}; + +template<typename K, typename V, typename H = std::hash<K>, typename E = std::equal_to<K>, typename A = std::allocator<std::pair<K, V> > > +class flat_hash_map + : public detailv3::sherwood_v3_table + < + std::pair<K, V>, + K, + H, + detailv3::KeyOrValueHasher<K, std::pair<K, V>, H>, + E, + detailv3::KeyOrValueEquality<K, std::pair<K, V>, E>, + A, + typename std::allocator_traits<A>::template rebind_alloc<detailv3::sherwood_v3_entry<std::pair<K, V>>> + > +{ + using Table = detailv3::sherwood_v3_table + < + std::pair<K, V>, + K, + H, + detailv3::KeyOrValueHasher<K, std::pair<K, V>, H>, + E, + detailv3::KeyOrValueEquality<K, std::pair<K, V>, E>, + A, + typename std::allocator_traits<A>::template rebind_alloc<detailv3::sherwood_v3_entry<std::pair<K, V>>> + >; +public: + + using key_type = K; + using mapped_type = V; + + using Table::Table; + flat_hash_map() + { + } + + inline V & operator[](const K & key) + { + return emplace(key, convertible_to_value()).first->second; + } + inline V & operator[](K && key) + { + return emplace(std::move(key), convertible_to_value()).first->second; + } + V & at(const K & key) + { + auto found = this->find(key); + if (found == this->end()) + throw std::out_of_range("Argument passed to at() was not in the map."); + return found->second; + } + const V & at(const K & key) const + { + auto found = this->find(key); + if (found == this->end()) + throw std::out_of_range("Argument passed to at() was not in the map."); + return found->second; + } + + using Table::emplace; + std::pair<typename Table::iterator, bool> emplace() + { + return emplace(key_type(), convertible_to_value()); + } + template<typename M> + std::pair<typename Table::iterator, bool> insert_or_assign(const key_type & key, M && m) + { + auto emplace_result = emplace(key, std::forward<M>(m)); + if (!emplace_result.second) + emplace_result.first->second = std::forward<M>(m); + return emplace_result; + } + template<typename M> + std::pair<typename Table::iterator, bool> insert_or_assign(key_type && key, M && m) + { + auto emplace_result = emplace(std::move(key), std::forward<M>(m)); + if (!emplace_result.second) + emplace_result.first->second = std::forward<M>(m); + return emplace_result; + } + template<typename M> + typename Table::iterator insert_or_assign(typename Table::const_iterator, const key_type & key, M && m) + { + return insert_or_assign(key, std::forward<M>(m)).first; + } + template<typename M> + typename Table::iterator insert_or_assign(typename Table::const_iterator, key_type && key, M && m) + { + return insert_or_assign(std::move(key), std::forward<M>(m)).first; + } + + friend bool operator==(const flat_hash_map & lhs, const flat_hash_map & rhs) + { + if (lhs.size() != rhs.size()) + return false; + for (const typename Table::value_type & value : lhs) + { + auto found = rhs.find(value.first); + if (found == rhs.end()) + return false; + else if (value.second != found->second) + return false; + } + return true; + } + friend bool operator!=(const flat_hash_map & lhs, const flat_hash_map & rhs) + { + return !(lhs == rhs); + } + +private: + struct convertible_to_value + { + operator V() const + { + return V(); + } + }; +}; + +template<typename T, typename H = std::hash<T>, typename E = std::equal_to<T>, typename A = std::allocator<T> > +class flat_hash_set + : public detailv3::sherwood_v3_table + < + T, + T, + H, + detailv3::functor_storage<size_t, H>, + E, + detailv3::functor_storage<bool, E>, + A, + typename std::allocator_traits<A>::template rebind_alloc<detailv3::sherwood_v3_entry<T>> + > +{ + using Table = detailv3::sherwood_v3_table + < + T, + T, + H, + detailv3::functor_storage<size_t, H>, + E, + detailv3::functor_storage<bool, E>, + A, + typename std::allocator_traits<A>::template rebind_alloc<detailv3::sherwood_v3_entry<T>> + >; +public: + + using key_type = T; + + using Table::Table; + flat_hash_set() + { + } + + template<typename... Args> + std::pair<typename Table::iterator, bool> emplace(Args &&... args) + { + return Table::emplace(T(std::forward<Args>(args)...)); + } + std::pair<typename Table::iterator, bool> emplace(const key_type & arg) + { + return Table::emplace(arg); + } + std::pair<typename Table::iterator, bool> emplace(key_type & arg) + { + return Table::emplace(arg); + } + std::pair<typename Table::iterator, bool> emplace(const key_type && arg) + { + return Table::emplace(std::move(arg)); + } + std::pair<typename Table::iterator, bool> emplace(key_type && arg) + { + return Table::emplace(std::move(arg)); + } + + friend bool operator==(const flat_hash_set & lhs, const flat_hash_set & rhs) + { + if (lhs.size() != rhs.size()) + return false; + for (const T & value : lhs) + { + if (rhs.find(value) == rhs.end()) + return false; + } + return true; + } + friend bool operator!=(const flat_hash_set & lhs, const flat_hash_set & rhs) + { + return !(lhs == rhs); + } +}; + + +template<typename T> +struct power_of_two_std_hash : std::hash<T> +{ + typedef ska::power_of_two_hash_policy hash_policy; +}; + +} // end namespace ska diff --git a/misc/benchmarks/external/tsl/robin_growth_policy.h b/misc/benchmarks/external/tsl/robin_growth_policy.h new file mode 100644 index 00000000..eba8cdfa --- /dev/null +++ b/misc/benchmarks/external/tsl/robin_growth_policy.h @@ -0,0 +1,406 @@ +/** + * MIT License + * + * Copyright (c) 2017 Thibaut Goetghebuer-Planchon <[email protected]> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef TSL_ROBIN_GROWTH_POLICY_H +#define TSL_ROBIN_GROWTH_POLICY_H + +#include <algorithm> +#include <array> +#include <climits> +#include <cmath> +#include <cstddef> +#include <cstdint> +#include <iterator> +#include <limits> +#include <ratio> +#include <stdexcept> + +#ifdef TSL_DEBUG +#define tsl_rh_assert(expr) assert(expr) +#else +#define tsl_rh_assert(expr) (static_cast<void>(0)) +#endif + +/** + * If exceptions are enabled, throw the exception passed in parameter, otherwise + * call std::terminate. + */ +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \ + (defined(_MSC_VER) && defined(_CPPUNWIND))) && \ + !defined(TSL_NO_EXCEPTIONS) +#define TSL_RH_THROW_OR_TERMINATE(ex, msg) throw ex(msg) +#else +#define TSL_RH_NO_EXCEPTIONS +#ifdef TSL_DEBUG +#include <iostream> +#define TSL_RH_THROW_OR_TERMINATE(ex, msg) \ + do { \ + std::cerr << msg << std::endl; \ + std::terminate(); \ + } while (0) +#else +#define TSL_RH_THROW_OR_TERMINATE(ex, msg) std::terminate() +#endif +#endif + +#if defined(__GNUC__) || defined(__clang__) +#define TSL_RH_LIKELY(exp) (__builtin_expect(!!(exp), true)) +#else +#define TSL_RH_LIKELY(exp) (exp) +#endif + +#define TSL_RH_UNUSED(x) static_cast<void>(x) + +namespace tsl { +namespace rh { + +/** + * Grow the hash table by a factor of GrowthFactor keeping the bucket count to a + * power of two. It allows the table to use a mask operation instead of a modulo + * operation to map a hash to a bucket. + * + * GrowthFactor must be a power of two >= 2. + */ +template <std::size_t GrowthFactor> +class power_of_two_growth_policy { + public: + /** + * Called on the hash table creation and on rehash. The number of buckets for + * the table is passed in parameter. This number is a minimum, the policy may + * update this value with a higher value if needed (but not lower). + * + * If 0 is given, min_bucket_count_in_out must still be 0 after the policy + * creation and bucket_for_hash must always return 0 in this case. + */ + explicit power_of_two_growth_policy(std::size_t& min_bucket_count_in_out) { + if (min_bucket_count_in_out > max_bucket_count()) { + TSL_RH_THROW_OR_TERMINATE(std::length_error, + "The hash table exceeds its maximum size."); + } + + if (min_bucket_count_in_out > 0) { + min_bucket_count_in_out = + round_up_to_power_of_two(min_bucket_count_in_out); + m_mask = min_bucket_count_in_out - 1; + } else { + m_mask = 0; + } + } + + /** + * Return the bucket [0, bucket_count()) to which the hash belongs. + * If bucket_count() is 0, it must always return 0. + */ + std::size_t bucket_for_hash(std::size_t hash) const noexcept { + return hash & m_mask; + } + + /** + * Return the number of buckets that should be used on next growth. + */ + std::size_t next_bucket_count() const { + if ((m_mask + 1) > max_bucket_count() / GrowthFactor) { + TSL_RH_THROW_OR_TERMINATE(std::length_error, + "The hash table exceeds its maximum size."); + } + + return (m_mask + 1) * GrowthFactor; + } + + /** + * Return the maximum number of buckets supported by the policy. + */ + std::size_t max_bucket_count() const { + // Largest power of two. + return (std::numeric_limits<std::size_t>::max() / 2) + 1; + } + + /** + * Reset the growth policy as if it was created with a bucket count of 0. + * After a clear, the policy must always return 0 when bucket_for_hash is + * called. + */ + void clear() noexcept { m_mask = 0; } + + private: + static std::size_t round_up_to_power_of_two(std::size_t value) { + if (is_power_of_two(value)) { + return value; + } + + if (value == 0) { + return 1; + } + + --value; + for (std::size_t i = 1; i < sizeof(std::size_t) * CHAR_BIT; i *= 2) { + value |= value >> i; + } + + return value + 1; + } + + static constexpr bool is_power_of_two(std::size_t value) { + return value != 0 && (value & (value - 1)) == 0; + } + + protected: + static_assert(is_power_of_two(GrowthFactor) && GrowthFactor >= 2, + "GrowthFactor must be a power of two >= 2."); + + std::size_t m_mask; +}; + +/** + * Grow the hash table by GrowthFactor::num / GrowthFactor::den and use a modulo + * to map a hash to a bucket. Slower but it can be useful if you want a slower + * growth. + */ +template <class GrowthFactor = std::ratio<3, 2>> +class mod_growth_policy { + public: + explicit mod_growth_policy(std::size_t& min_bucket_count_in_out) { + if (min_bucket_count_in_out > max_bucket_count()) { + TSL_RH_THROW_OR_TERMINATE(std::length_error, + "The hash table exceeds its maximum size."); + } + + if (min_bucket_count_in_out > 0) { + m_mod = min_bucket_count_in_out; + } else { + m_mod = 1; + } + } + + std::size_t bucket_for_hash(std::size_t hash) const noexcept { + return hash % m_mod; + } + + std::size_t next_bucket_count() const { + if (m_mod == max_bucket_count()) { + TSL_RH_THROW_OR_TERMINATE(std::length_error, + "The hash table exceeds its maximum size."); + } + + const double next_bucket_count = + std::ceil(double(m_mod) * REHASH_SIZE_MULTIPLICATION_FACTOR); + if (!std::isnormal(next_bucket_count)) { + TSL_RH_THROW_OR_TERMINATE(std::length_error, + "The hash table exceeds its maximum size."); + } + + if (next_bucket_count > double(max_bucket_count())) { + return max_bucket_count(); + } else { + return std::size_t(next_bucket_count); + } + } + + std::size_t max_bucket_count() const { return MAX_BUCKET_COUNT; } + + void clear() noexcept { m_mod = 1; } + + private: + static constexpr double REHASH_SIZE_MULTIPLICATION_FACTOR = + 1.0 * GrowthFactor::num / GrowthFactor::den; + static const std::size_t MAX_BUCKET_COUNT = + std::size_t(double(std::numeric_limits<std::size_t>::max() / + REHASH_SIZE_MULTIPLICATION_FACTOR)); + + static_assert(REHASH_SIZE_MULTIPLICATION_FACTOR >= 1.1, + "Growth factor should be >= 1.1."); + + std::size_t m_mod; +}; + +namespace detail { + +#if SIZE_MAX >= ULLONG_MAX +#define TSL_RH_NB_PRIMES 51 +#elif SIZE_MAX >= ULONG_MAX +#define TSL_RH_NB_PRIMES 40 +#else +#define TSL_RH_NB_PRIMES 23 +#endif + +static constexpr const std::array<std::size_t, TSL_RH_NB_PRIMES> PRIMES = {{ + 1u, + 5u, + 17u, + 29u, + 37u, + 53u, + 67u, + 79u, + 97u, + 131u, + 193u, + 257u, + 389u, + 521u, + 769u, + 1031u, + 1543u, + 2053u, + 3079u, + 6151u, + 12289u, + 24593u, + 49157u, +#if SIZE_MAX >= ULONG_MAX + 98317ul, + 196613ul, + 393241ul, + 786433ul, + 1572869ul, + 3145739ul, + 6291469ul, + 12582917ul, + 25165843ul, + 50331653ul, + 100663319ul, + 201326611ul, + 402653189ul, + 805306457ul, + 1610612741ul, + 3221225473ul, + 4294967291ul, +#endif +#if SIZE_MAX >= ULLONG_MAX + 6442450939ull, + 12884901893ull, + 25769803751ull, + 51539607551ull, + 103079215111ull, + 206158430209ull, + 412316860441ull, + 824633720831ull, + 1649267441651ull, + 3298534883309ull, + 6597069766657ull, +#endif +}}; + +template <unsigned int IPrime> +static constexpr std::size_t mod(std::size_t hash) { + return hash % PRIMES[IPrime]; +} + +// MOD_PRIME[iprime](hash) returns hash % PRIMES[iprime]. This table allows for +// faster modulo as the compiler can optimize the modulo code better with a +// constant known at the compilation. +static constexpr const std::array<std::size_t (*)(std::size_t), + TSL_RH_NB_PRIMES> + MOD_PRIME = {{ + &mod<0>, &mod<1>, &mod<2>, &mod<3>, &mod<4>, &mod<5>, + &mod<6>, &mod<7>, &mod<8>, &mod<9>, &mod<10>, &mod<11>, + &mod<12>, &mod<13>, &mod<14>, &mod<15>, &mod<16>, &mod<17>, + &mod<18>, &mod<19>, &mod<20>, &mod<21>, &mod<22>, +#if SIZE_MAX >= ULONG_MAX + &mod<23>, &mod<24>, &mod<25>, &mod<26>, &mod<27>, &mod<28>, + &mod<29>, &mod<30>, &mod<31>, &mod<32>, &mod<33>, &mod<34>, + &mod<35>, &mod<36>, &mod<37>, &mod<38>, &mod<39>, +#endif +#if SIZE_MAX >= ULLONG_MAX + &mod<40>, &mod<41>, &mod<42>, &mod<43>, &mod<44>, &mod<45>, + &mod<46>, &mod<47>, &mod<48>, &mod<49>, &mod<50>, +#endif + }}; + +} // namespace detail + +/** + * Grow the hash table by using prime numbers as bucket count. Slower than + * tsl::rh::power_of_two_growth_policy in general but will probably distribute + * the values around better in the buckets with a poor hash function. + * + * To allow the compiler to optimize the modulo operation, a lookup table is + * used with constant primes numbers. + * + * With a switch the code would look like: + * \code + * switch(iprime) { // iprime is the current prime of the hash table + * case 0: hash % 5ul; + * break; + * case 1: hash % 17ul; + * break; + * case 2: hash % 29ul; + * break; + * ... + * } + * \endcode + * + * Due to the constant variable in the modulo the compiler is able to optimize + * the operation by a series of multiplications, substractions and shifts. + * + * The 'hash % 5' could become something like 'hash - (hash * 0xCCCCCCCD) >> 34) + * * 5' in a 64 bits environment. + */ +class prime_growth_policy { + public: + explicit prime_growth_policy(std::size_t& min_bucket_count_in_out) { + auto it_prime = std::lower_bound( + detail::PRIMES.begin(), detail::PRIMES.end(), min_bucket_count_in_out); + if (it_prime == detail::PRIMES.end()) { + TSL_RH_THROW_OR_TERMINATE(std::length_error, + "The hash table exceeds its maximum size."); + } + + m_iprime = static_cast<unsigned int>( + std::distance(detail::PRIMES.begin(), it_prime)); + if (min_bucket_count_in_out > 0) { + min_bucket_count_in_out = *it_prime; + } else { + min_bucket_count_in_out = 0; + } + } + + std::size_t bucket_for_hash(std::size_t hash) const noexcept { + return detail::MOD_PRIME[m_iprime](hash); + } + + std::size_t next_bucket_count() const { + if (m_iprime + 1 >= detail::PRIMES.size()) { + TSL_RH_THROW_OR_TERMINATE(std::length_error, + "The hash table exceeds its maximum size."); + } + + return detail::PRIMES[m_iprime + 1]; + } + + std::size_t max_bucket_count() const { return detail::PRIMES.back(); } + + void clear() noexcept { m_iprime = 0; } + + private: + unsigned int m_iprime; + + static_assert(std::numeric_limits<decltype(m_iprime)>::max() >= + detail::PRIMES.size(), + "The type of m_iprime is not big enough."); +}; + +} // namespace rh +} // namespace tsl + +#endif diff --git a/misc/benchmarks/external/tsl/robin_hash.h b/misc/benchmarks/external/tsl/robin_hash.h new file mode 100644 index 00000000..89c7c96f --- /dev/null +++ b/misc/benchmarks/external/tsl/robin_hash.h @@ -0,0 +1,1639 @@ +/** + * MIT License + * + * Copyright (c) 2017 Thibaut Goetghebuer-Planchon <[email protected]> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef TSL_ROBIN_HASH_H +#define TSL_ROBIN_HASH_H + +#include <algorithm> +#include <cassert> +#include <cmath> +#include <cstddef> +#include <cstdint> +#include <exception> +#include <iterator> +#include <limits> +#include <memory> +#include <new> +#include <stdexcept> +#include <tuple> +#include <type_traits> +#include <utility> +#include <vector> + +#include "robin_growth_policy.h" + +namespace tsl { + +namespace detail_robin_hash { + +template <typename T> +struct make_void { + using type = void; +}; + +template <typename T, typename = void> +struct has_is_transparent : std::false_type {}; + +template <typename T> +struct has_is_transparent<T, + typename make_void<typename T::is_transparent>::type> + : std::true_type {}; + +template <typename U> +struct is_power_of_two_policy : std::false_type {}; + +template <std::size_t GrowthFactor> +struct is_power_of_two_policy<tsl::rh::power_of_two_growth_policy<GrowthFactor>> + : std::true_type {}; + +// Only available in C++17, we need to be compatible with C++11 +template <class T> +const T& clamp(const T& v, const T& lo, const T& hi) { + return std::min(hi, std::max(lo, v)); +} + +template <typename T, typename U> +static T numeric_cast(U value, + const char* error_message = "numeric_cast() failed.") { + T ret = static_cast<T>(value); + if (static_cast<U>(ret) != value) { + TSL_RH_THROW_OR_TERMINATE(std::runtime_error, error_message); + } + + const bool is_same_signedness = + (std::is_unsigned<T>::value && std::is_unsigned<U>::value) || + (std::is_signed<T>::value && std::is_signed<U>::value); + if (!is_same_signedness && (ret < T{}) != (value < U{})) { + TSL_RH_THROW_OR_TERMINATE(std::runtime_error, error_message); + } + + return ret; +} + +template <class T, class Deserializer> +static T deserialize_value(Deserializer& deserializer) { + // MSVC < 2017 is not conformant, circumvent the problem by removing the + // template keyword +#if defined(_MSC_VER) && _MSC_VER < 1910 + return deserializer.Deserializer::operator()<T>(); +#else + return deserializer.Deserializer::template operator()<T>(); +#endif +} + +/** + * Fixed size type used to represent size_type values on serialization. Need to + * be big enough to represent a std::size_t on 32 and 64 bits platforms, and + * must be the same size on both platforms. + */ +using slz_size_type = std::uint64_t; +static_assert(std::numeric_limits<slz_size_type>::max() >= + std::numeric_limits<std::size_t>::max(), + "slz_size_type must be >= std::size_t"); + +using truncated_hash_type = std::uint32_t; + +/** + * Helper class that stores a truncated hash if StoreHash is true and nothing + * otherwise. + */ +template <bool StoreHash> +class bucket_entry_hash { + public: + bool bucket_hash_equal(std::size_t /*hash*/) const noexcept { return true; } + + truncated_hash_type truncated_hash() const noexcept { return 0; } + + protected: + void set_hash(truncated_hash_type /*hash*/) noexcept {} +}; + +template <> +class bucket_entry_hash<true> { + public: + bool bucket_hash_equal(std::size_t hash) const noexcept { + return m_hash == truncated_hash_type(hash); + } + + truncated_hash_type truncated_hash() const noexcept { return m_hash; } + + protected: + void set_hash(truncated_hash_type hash) noexcept { + m_hash = truncated_hash_type(hash); + } + + private: + truncated_hash_type m_hash; +}; + +/** + * Each bucket entry has: + * - A value of type `ValueType`. + * - An integer to store how far the value of the bucket, if any, is from its + * ideal bucket (ex: if the current bucket 5 has the value 'foo' and + * `hash('foo') % nb_buckets` == 3, `dist_from_ideal_bucket()` will return 2 as + * the current value of the bucket is two buckets away from its ideal bucket) If + * there is no value in the bucket (i.e. `empty()` is true) + * `dist_from_ideal_bucket()` will be < 0. + * - A marker which tells us if the bucket is the last bucket of the bucket + * array (useful for the iterator of the hash table). + * - If `StoreHash` is true, 32 bits of the hash of the value, if any, are also + * stored in the bucket. If the size of the hash is more than 32 bits, it is + * truncated. We don't store the full hash as storing the hash is a potential + * opportunity to use the unused space due to the alignment of the bucket_entry + * structure. We can thus potentially store the hash without any extra space + * (which would not be possible with 64 bits of the hash). + */ +template <typename ValueType, bool StoreHash> +class bucket_entry : public bucket_entry_hash<StoreHash> { + using bucket_hash = bucket_entry_hash<StoreHash>; + + public: + using value_type = ValueType; + using distance_type = std::int16_t; + + bucket_entry() noexcept + : bucket_hash(), + m_dist_from_ideal_bucket(EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET), + m_last_bucket(false) { + tsl_rh_assert(empty()); + } + + bucket_entry(bool last_bucket) noexcept + : bucket_hash(), + m_dist_from_ideal_bucket(EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET), + m_last_bucket(last_bucket) { + tsl_rh_assert(empty()); + } + + bucket_entry(const bucket_entry& other) noexcept( + std::is_nothrow_copy_constructible<value_type>::value) + : bucket_hash(other), + m_dist_from_ideal_bucket(EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET), + m_last_bucket(other.m_last_bucket) { + if (!other.empty()) { + ::new (static_cast<void*>(std::addressof(m_value))) + value_type(other.value()); + m_dist_from_ideal_bucket = other.m_dist_from_ideal_bucket; + } + tsl_rh_assert(empty() == other.empty()); + } + + /** + * Never really used, but still necessary as we must call resize on an empty + * `std::vector<bucket_entry>`. and we need to support move-only types. See + * robin_hash constructor for details. + */ + bucket_entry(bucket_entry&& other) noexcept( + std::is_nothrow_move_constructible<value_type>::value) + : bucket_hash(std::move(other)), + m_dist_from_ideal_bucket(EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET), + m_last_bucket(other.m_last_bucket) { + if (!other.empty()) { + ::new (static_cast<void*>(std::addressof(m_value))) + value_type(std::move(other.value())); + m_dist_from_ideal_bucket = other.m_dist_from_ideal_bucket; + } + tsl_rh_assert(empty() == other.empty()); + } + + bucket_entry& operator=(const bucket_entry& other) noexcept( + std::is_nothrow_copy_constructible<value_type>::value) { + if (this != &other) { + clear(); + + bucket_hash::operator=(other); + if (!other.empty()) { + ::new (static_cast<void*>(std::addressof(m_value))) + value_type(other.value()); + } + + m_dist_from_ideal_bucket = other.m_dist_from_ideal_bucket; + m_last_bucket = other.m_last_bucket; + } + + return *this; + } + + bucket_entry& operator=(bucket_entry&&) = delete; + + ~bucket_entry() noexcept { clear(); } + + void clear() noexcept { + if (!empty()) { + destroy_value(); + m_dist_from_ideal_bucket = EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET; + } + } + + bool empty() const noexcept { + return m_dist_from_ideal_bucket == EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET; + } + + value_type& value() noexcept { + tsl_rh_assert(!empty()); +#if defined(__cplusplus) && __cplusplus >= 201703L + return *std::launder( + reinterpret_cast<value_type*>(std::addressof(m_value))); +#else + return *reinterpret_cast<value_type*>(std::addressof(m_value)); +#endif + } + + const value_type& value() const noexcept { + tsl_rh_assert(!empty()); +#if defined(__cplusplus) && __cplusplus >= 201703L + return *std::launder( + reinterpret_cast<const value_type*>(std::addressof(m_value))); +#else + return *reinterpret_cast<const value_type*>(std::addressof(m_value)); +#endif + } + + distance_type dist_from_ideal_bucket() const noexcept { + return m_dist_from_ideal_bucket; + } + + bool last_bucket() const noexcept { return m_last_bucket; } + + void set_as_last_bucket() noexcept { m_last_bucket = true; } + + template <typename... Args> + void set_value_of_empty_bucket(distance_type dist_from_ideal_bucket, + truncated_hash_type hash, + Args&&... value_type_args) { + tsl_rh_assert(dist_from_ideal_bucket >= 0); + tsl_rh_assert(empty()); + + ::new (static_cast<void*>(std::addressof(m_value))) + value_type(std::forward<Args>(value_type_args)...); + this->set_hash(hash); + m_dist_from_ideal_bucket = dist_from_ideal_bucket; + + tsl_rh_assert(!empty()); + } + + void swap_with_value_in_bucket(distance_type& dist_from_ideal_bucket, + truncated_hash_type& hash, value_type& value) { + tsl_rh_assert(!empty()); + tsl_rh_assert(dist_from_ideal_bucket > m_dist_from_ideal_bucket); + + using std::swap; + swap(value, this->value()); + swap(dist_from_ideal_bucket, m_dist_from_ideal_bucket); + + if (StoreHash) { + const truncated_hash_type tmp_hash = this->truncated_hash(); + this->set_hash(hash); + hash = tmp_hash; + } else { + // Avoid warning of unused variable if StoreHash is false + TSL_RH_UNUSED(hash); + } + } + + static truncated_hash_type truncate_hash(std::size_t hash) noexcept { + return truncated_hash_type(hash); + } + + private: + void destroy_value() noexcept { + tsl_rh_assert(!empty()); + value().~value_type(); + } + + public: + static const distance_type EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET = -1; + static const distance_type DIST_FROM_IDEAL_BUCKET_LIMIT = 4096; + static_assert(DIST_FROM_IDEAL_BUCKET_LIMIT <= + std::numeric_limits<distance_type>::max() - 1, + "DIST_FROM_IDEAL_BUCKET_LIMIT must be <= " + "std::numeric_limits<distance_type>::max() - 1."); + + private: + using storage = typename std::aligned_storage<sizeof(value_type), + alignof(value_type)>::type; + + distance_type m_dist_from_ideal_bucket; + bool m_last_bucket; + storage m_value; +}; + +/** + * Internal common class used by `robin_map` and `robin_set`. + * + * ValueType is what will be stored by `robin_hash` (usually `std::pair<Key, T>` + * for map and `Key` for set). + * + * `KeySelect` should be a `FunctionObject` which takes a `ValueType` in + * parameter and returns a reference to the key. + * + * `ValueSelect` should be a `FunctionObject` which takes a `ValueType` in + * parameter and returns a reference to the value. `ValueSelect` should be void + * if there is no value (in a set for example). + * + * The strong exception guarantee only holds if the expression + * `std::is_nothrow_swappable<ValueType>::value && + * std::is_nothrow_move_constructible<ValueType>::value` is true. + * + * Behaviour is undefined if the destructor of `ValueType` throws. + */ +template <class ValueType, class KeySelect, class ValueSelect, class Hash, + class KeyEqual, class Allocator, bool StoreHash, class GrowthPolicy> +class robin_hash : private Hash, private KeyEqual, private GrowthPolicy { + private: + template <typename U> + using has_mapped_type = + typename std::integral_constant<bool, !std::is_same<U, void>::value>; + + static_assert( + noexcept(std::declval<GrowthPolicy>().bucket_for_hash(std::size_t(0))), + "GrowthPolicy::bucket_for_hash must be noexcept."); + static_assert(noexcept(std::declval<GrowthPolicy>().clear()), + "GrowthPolicy::clear must be noexcept."); + + public: + template <bool IsConst> + class robin_iterator; + + using key_type = typename KeySelect::key_type; + using value_type = ValueType; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using hasher = Hash; + using key_equal = KeyEqual; + using allocator_type = Allocator; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = value_type*; + using const_pointer = const value_type*; + using iterator = robin_iterator<false>; + using const_iterator = robin_iterator<true>; + + private: + /** + * Either store the hash because we are asked by the `StoreHash` template + * parameter or store the hash because it doesn't cost us anything in size and + * can be used to speed up rehash. + */ + static constexpr bool STORE_HASH = + StoreHash || + ((sizeof(tsl::detail_robin_hash::bucket_entry<value_type, true>) == + sizeof(tsl::detail_robin_hash::bucket_entry<value_type, false>)) && + (sizeof(std::size_t) == sizeof(truncated_hash_type) || + is_power_of_two_policy<GrowthPolicy>::value) && + // Don't store the hash for primitive types with default hash. + (!std::is_arithmetic<key_type>::value || + !std::is_same<Hash, std::hash<key_type>>::value)); + + /** + * Only use the stored hash on lookup if we are explicitly asked. We are not + * sure how slow the KeyEqual operation is. An extra comparison may slow + * things down with a fast KeyEqual. + */ + static constexpr bool USE_STORED_HASH_ON_LOOKUP = StoreHash; + + /** + * We can only use the hash on rehash if the size of the hash type is the same + * as the stored one or if we use a power of two modulo. In the case of the + * power of two modulo, we just mask the least significant bytes, we just have + * to check that the truncated_hash_type didn't truncated more bytes. + */ + static bool USE_STORED_HASH_ON_REHASH(size_type bucket_count) { + if (STORE_HASH && sizeof(std::size_t) == sizeof(truncated_hash_type)) { + TSL_RH_UNUSED(bucket_count); + return true; + } else if (STORE_HASH && is_power_of_two_policy<GrowthPolicy>::value) { + return bucket_count == 0 || + (bucket_count - 1) <= + std::numeric_limits<truncated_hash_type>::max(); + } else { + TSL_RH_UNUSED(bucket_count); + return false; + } + } + + using bucket_entry = + tsl::detail_robin_hash::bucket_entry<value_type, STORE_HASH>; + using distance_type = typename bucket_entry::distance_type; + + using buckets_allocator = typename std::allocator_traits< + allocator_type>::template rebind_alloc<bucket_entry>; + using buckets_container_type = std::vector<bucket_entry, buckets_allocator>; + + public: + /** + * The 'operator*()' and 'operator->()' methods return a const reference and + * const pointer respectively to the stored value type. + * + * In case of a map, to get a mutable reference to the value associated to a + * key (the '.second' in the stored pair), you have to call 'value()'. + * + * The main reason for this is that if we returned a `std::pair<Key, T>&` + * instead of a `const std::pair<Key, T>&`, the user may modify the key which + * will put the map in a undefined state. + */ + template <bool IsConst> + class robin_iterator { + friend class robin_hash; + + private: + using bucket_entry_ptr = + typename std::conditional<IsConst, const bucket_entry*, + bucket_entry*>::type; + + robin_iterator(bucket_entry_ptr bucket) noexcept : m_bucket(bucket) {} + + public: + using iterator_category = std::forward_iterator_tag; + using value_type = const typename robin_hash::value_type; + using difference_type = std::ptrdiff_t; + using reference = value_type&; + using pointer = value_type*; + + robin_iterator() noexcept {} + + // Copy constructor from iterator to const_iterator. + template <bool TIsConst = IsConst, + typename std::enable_if<TIsConst>::type* = nullptr> + robin_iterator(const robin_iterator<!TIsConst>& other) noexcept + : m_bucket(other.m_bucket) {} + + robin_iterator(const robin_iterator& other) = default; + robin_iterator(robin_iterator&& other) = default; + robin_iterator& operator=(const robin_iterator& other) = default; + robin_iterator& operator=(robin_iterator&& other) = default; + + const typename robin_hash::key_type& key() const { + return KeySelect()(m_bucket->value()); + } + + template <class U = ValueSelect, + typename std::enable_if<has_mapped_type<U>::value && + IsConst>::type* = nullptr> + const typename U::value_type& value() const { + return U()(m_bucket->value()); + } + + template <class U = ValueSelect, + typename std::enable_if<has_mapped_type<U>::value && + !IsConst>::type* = nullptr> + typename U::value_type& value() const { + return U()(m_bucket->value()); + } + + reference operator*() const { return m_bucket->value(); } + + pointer operator->() const { return std::addressof(m_bucket->value()); } + + robin_iterator& operator++() { + while (true) { + if (m_bucket->last_bucket()) { + ++m_bucket; + return *this; + } + + ++m_bucket; + if (!m_bucket->empty()) { + return *this; + } + } + } + + robin_iterator operator++(int) { + robin_iterator tmp(*this); + ++*this; + + return tmp; + } + + friend bool operator==(const robin_iterator& lhs, + const robin_iterator& rhs) { + return lhs.m_bucket == rhs.m_bucket; + } + + friend bool operator!=(const robin_iterator& lhs, + const robin_iterator& rhs) { + return !(lhs == rhs); + } + + private: + bucket_entry_ptr m_bucket; + }; + + public: +#if defined(__cplusplus) && __cplusplus >= 201402L + robin_hash(size_type bucket_count, const Hash& hash, const KeyEqual& equal, + const Allocator& alloc, + float min_load_factor = DEFAULT_MIN_LOAD_FACTOR, + float max_load_factor = DEFAULT_MAX_LOAD_FACTOR) + : Hash(hash), + KeyEqual(equal), + GrowthPolicy(bucket_count), + m_buckets_data(bucket_count, alloc), + m_buckets(m_buckets_data.empty() ? static_empty_bucket_ptr() + : m_buckets_data.data()), + m_bucket_count(bucket_count), + m_nb_elements(0), + m_grow_on_next_insert(false), + m_try_shrink_on_next_insert(false) { + if (bucket_count > max_bucket_count()) { + TSL_RH_THROW_OR_TERMINATE(std::length_error, + "The map exceeds its maximum bucket count."); + } + + if (m_bucket_count > 0) { + tsl_rh_assert(!m_buckets_data.empty()); + m_buckets_data.back().set_as_last_bucket(); + } + + this->min_load_factor(min_load_factor); + this->max_load_factor(max_load_factor); + } +#else + /** + * C++11 doesn't support the creation of a std::vector with a custom allocator + * and 'count' default-inserted elements. The needed contructor `explicit + * vector(size_type count, const Allocator& alloc = Allocator());` is only + * available in C++14 and later. We thus must resize after using the + * `vector(const Allocator& alloc)` constructor. + * + * We can't use `vector(size_type count, const T& value, const Allocator& + * alloc)` as it requires the value T to be copyable. + */ + robin_hash(size_type bucket_count, const Hash& hash, const KeyEqual& equal, + const Allocator& alloc, + float min_load_factor = DEFAULT_MIN_LOAD_FACTOR, + float max_load_factor = DEFAULT_MAX_LOAD_FACTOR) + : Hash(hash), + KeyEqual(equal), + GrowthPolicy(bucket_count), + m_buckets_data(alloc), + m_buckets(static_empty_bucket_ptr()), + m_bucket_count(bucket_count), + m_nb_elements(0), + m_grow_on_next_insert(false), + m_try_shrink_on_next_insert(false) { + if (bucket_count > max_bucket_count()) { + TSL_RH_THROW_OR_TERMINATE(std::length_error, + "The map exceeds its maximum bucket count."); + } + + if (m_bucket_count > 0) { + m_buckets_data.resize(m_bucket_count); + m_buckets = m_buckets_data.data(); + + tsl_rh_assert(!m_buckets_data.empty()); + m_buckets_data.back().set_as_last_bucket(); + } + + this->min_load_factor(min_load_factor); + this->max_load_factor(max_load_factor); + } +#endif + + robin_hash(const robin_hash& other) + : Hash(other), + KeyEqual(other), + GrowthPolicy(other), + m_buckets_data(other.m_buckets_data), + m_buckets(m_buckets_data.empty() ? static_empty_bucket_ptr() + : m_buckets_data.data()), + m_bucket_count(other.m_bucket_count), + m_nb_elements(other.m_nb_elements), + m_load_threshold(other.m_load_threshold), + m_min_load_factor(other.m_min_load_factor), + m_max_load_factor(other.m_max_load_factor), + m_grow_on_next_insert(other.m_grow_on_next_insert), + m_try_shrink_on_next_insert(other.m_try_shrink_on_next_insert) {} + + robin_hash(robin_hash&& other) noexcept( + std::is_nothrow_move_constructible< + Hash>::value&& std::is_nothrow_move_constructible<KeyEqual>::value&& + std::is_nothrow_move_constructible<GrowthPolicy>::value&& + std::is_nothrow_move_constructible<buckets_container_type>::value) + : Hash(std::move(static_cast<Hash&>(other))), + KeyEqual(std::move(static_cast<KeyEqual&>(other))), + GrowthPolicy(std::move(static_cast<GrowthPolicy&>(other))), + m_buckets_data(std::move(other.m_buckets_data)), + m_buckets(m_buckets_data.empty() ? static_empty_bucket_ptr() + : m_buckets_data.data()), + m_bucket_count(other.m_bucket_count), + m_nb_elements(other.m_nb_elements), + m_load_threshold(other.m_load_threshold), + m_min_load_factor(other.m_min_load_factor), + m_max_load_factor(other.m_max_load_factor), + m_grow_on_next_insert(other.m_grow_on_next_insert), + m_try_shrink_on_next_insert(other.m_try_shrink_on_next_insert) { + other.clear_and_shrink(); + } + + robin_hash& operator=(const robin_hash& other) { + if (&other != this) { + Hash::operator=(other); + KeyEqual::operator=(other); + GrowthPolicy::operator=(other); + + m_buckets_data = other.m_buckets_data; + m_buckets = m_buckets_data.empty() ? static_empty_bucket_ptr() + : m_buckets_data.data(); + m_bucket_count = other.m_bucket_count; + m_nb_elements = other.m_nb_elements; + + m_load_threshold = other.m_load_threshold; + m_min_load_factor = other.m_min_load_factor; + m_max_load_factor = other.m_max_load_factor; + + m_grow_on_next_insert = other.m_grow_on_next_insert; + m_try_shrink_on_next_insert = other.m_try_shrink_on_next_insert; + } + + return *this; + } + + robin_hash& operator=(robin_hash&& other) { + other.swap(*this); + other.clear_and_shrink(); + + return *this; + } + + allocator_type get_allocator() const { + return m_buckets_data.get_allocator(); + } + + /* + * Iterators + */ + iterator begin() noexcept { + std::size_t i = 0; + while (i < m_bucket_count && m_buckets[i].empty()) { + i++; + } + + return iterator(m_buckets + i); + } + + const_iterator begin() const noexcept { return cbegin(); } + + const_iterator cbegin() const noexcept { + std::size_t i = 0; + while (i < m_bucket_count && m_buckets[i].empty()) { + i++; + } + + return const_iterator(m_buckets + i); + } + + iterator end() noexcept { return iterator(m_buckets + m_bucket_count); } + + const_iterator end() const noexcept { return cend(); } + + const_iterator cend() const noexcept { + return const_iterator(m_buckets + m_bucket_count); + } + + /* + * Capacity + */ + bool empty() const noexcept { return m_nb_elements == 0; } + + size_type size() const noexcept { return m_nb_elements; } + + size_type max_size() const noexcept { return m_buckets_data.max_size(); } + + /* + * Modifiers + */ + void clear() noexcept { + if (m_min_load_factor > 0.0f) { + clear_and_shrink(); + } else { + for (auto& bucket : m_buckets_data) { + bucket.clear(); + } + + m_nb_elements = 0; + m_grow_on_next_insert = false; + } + } + + template <typename P> + std::pair<iterator, bool> insert(P&& value) { + return insert_impl(KeySelect()(value), std::forward<P>(value)); + } + + template <typename P> + iterator insert_hint(const_iterator hint, P&& value) { + if (hint != cend() && + compare_keys(KeySelect()(*hint), KeySelect()(value))) { + return mutable_iterator(hint); + } + + return insert(std::forward<P>(value)).first; + } + + template <class InputIt> + void insert(InputIt first, InputIt last) { + if (std::is_base_of< + std::forward_iterator_tag, + typename std::iterator_traits<InputIt>::iterator_category>::value) { + const auto nb_elements_insert = std::distance(first, last); + const size_type nb_free_buckets = m_load_threshold - size(); + tsl_rh_assert(m_load_threshold >= size()); + + if (nb_elements_insert > 0 && + nb_free_buckets < size_type(nb_elements_insert)) { + reserve(size() + size_type(nb_elements_insert)); + } + } + + for (; first != last; ++first) { + insert(*first); + } + } + + template <class K, class M> + std::pair<iterator, bool> insert_or_assign(K&& key, M&& obj) { + auto it = try_emplace(std::forward<K>(key), std::forward<M>(obj)); + if (!it.second) { + it.first.value() = std::forward<M>(obj); + } + + return it; + } + + template <class K, class M> + iterator insert_or_assign(const_iterator hint, K&& key, M&& obj) { + if (hint != cend() && compare_keys(KeySelect()(*hint), key)) { + auto it = mutable_iterator(hint); + it.value() = std::forward<M>(obj); + + return it; + } + + return insert_or_assign(std::forward<K>(key), std::forward<M>(obj)).first; + } + + template <class... Args> + std::pair<iterator, bool> emplace(Args&&... args) { + return insert(value_type(std::forward<Args>(args)...)); + } + + template <class... Args> + iterator emplace_hint(const_iterator hint, Args&&... args) { + return insert_hint(hint, value_type(std::forward<Args>(args)...)); + } + + template <class K, class... Args> + std::pair<iterator, bool> try_emplace(K&& key, Args&&... args) { + return insert_impl(key, std::piecewise_construct, + std::forward_as_tuple(std::forward<K>(key)), + std::forward_as_tuple(std::forward<Args>(args)...)); + } + + template <class K, class... Args> + iterator try_emplace_hint(const_iterator hint, K&& key, Args&&... args) { + if (hint != cend() && compare_keys(KeySelect()(*hint), key)) { + return mutable_iterator(hint); + } + + return try_emplace(std::forward<K>(key), std::forward<Args>(args)...).first; + } + + /** + * Here to avoid `template<class K> size_type erase(const K& key)` being used + * when we use an `iterator` instead of a `const_iterator`. + */ + iterator erase(iterator pos) { + erase_from_bucket(pos); + + /** + * Erase bucket used a backward shift after clearing the bucket. + * Check if there is a new value in the bucket, if not get the next + * non-empty. + */ + if (pos.m_bucket->empty()) { + ++pos; + } + + m_try_shrink_on_next_insert = true; + + return pos; + } + + iterator erase(const_iterator pos) { return erase(mutable_iterator(pos)); } + + iterator erase(const_iterator first, const_iterator last) { + if (first == last) { + return mutable_iterator(first); + } + + auto first_mutable = mutable_iterator(first); + auto last_mutable = mutable_iterator(last); + for (auto it = first_mutable.m_bucket; it != last_mutable.m_bucket; ++it) { + if (!it->empty()) { + it->clear(); + m_nb_elements--; + } + } + + if (last_mutable == end()) { + m_try_shrink_on_next_insert = true; + return end(); + } + + /* + * Backward shift on the values which come after the deleted values. + * We try to move the values closer to their ideal bucket. + */ + std::size_t icloser_bucket = + static_cast<std::size_t>(first_mutable.m_bucket - m_buckets); + std::size_t ito_move_closer_value = + static_cast<std::size_t>(last_mutable.m_bucket - m_buckets); + tsl_rh_assert(ito_move_closer_value > icloser_bucket); + + const std::size_t ireturn_bucket = + ito_move_closer_value - + std::min( + ito_move_closer_value - icloser_bucket, + std::size_t( + m_buckets[ito_move_closer_value].dist_from_ideal_bucket())); + + while (ito_move_closer_value < m_bucket_count && + m_buckets[ito_move_closer_value].dist_from_ideal_bucket() > 0) { + icloser_bucket = + ito_move_closer_value - + std::min( + ito_move_closer_value - icloser_bucket, + std::size_t( + m_buckets[ito_move_closer_value].dist_from_ideal_bucket())); + + tsl_rh_assert(m_buckets[icloser_bucket].empty()); + const distance_type new_distance = distance_type( + m_buckets[ito_move_closer_value].dist_from_ideal_bucket() - + (ito_move_closer_value - icloser_bucket)); + m_buckets[icloser_bucket].set_value_of_empty_bucket( + new_distance, m_buckets[ito_move_closer_value].truncated_hash(), + std::move(m_buckets[ito_move_closer_value].value())); + m_buckets[ito_move_closer_value].clear(); + + ++icloser_bucket; + ++ito_move_closer_value; + } + + m_try_shrink_on_next_insert = true; + + return iterator(m_buckets + ireturn_bucket); + } + + template <class K> + size_type erase(const K& key) { + return erase(key, hash_key(key)); + } + + template <class K> + size_type erase(const K& key, std::size_t hash) { + auto it = find(key, hash); + if (it != end()) { + erase_from_bucket(it); + m_try_shrink_on_next_insert = true; + + return 1; + } else { + return 0; + } + } + + void swap(robin_hash& other) { + using std::swap; + + swap(static_cast<Hash&>(*this), static_cast<Hash&>(other)); + swap(static_cast<KeyEqual&>(*this), static_cast<KeyEqual&>(other)); + swap(static_cast<GrowthPolicy&>(*this), static_cast<GrowthPolicy&>(other)); + swap(m_buckets_data, other.m_buckets_data); + swap(m_buckets, other.m_buckets); + swap(m_bucket_count, other.m_bucket_count); + swap(m_nb_elements, other.m_nb_elements); + swap(m_load_threshold, other.m_load_threshold); + swap(m_min_load_factor, other.m_min_load_factor); + swap(m_max_load_factor, other.m_max_load_factor); + swap(m_grow_on_next_insert, other.m_grow_on_next_insert); + swap(m_try_shrink_on_next_insert, other.m_try_shrink_on_next_insert); + } + + /* + * Lookup + */ + template <class K, class U = ValueSelect, + typename std::enable_if<has_mapped_type<U>::value>::type* = nullptr> + typename U::value_type& at(const K& key) { + return at(key, hash_key(key)); + } + + template <class K, class U = ValueSelect, + typename std::enable_if<has_mapped_type<U>::value>::type* = nullptr> + typename U::value_type& at(const K& key, std::size_t hash) { + return const_cast<typename U::value_type&>( + static_cast<const robin_hash*>(this)->at(key, hash)); + } + + template <class K, class U = ValueSelect, + typename std::enable_if<has_mapped_type<U>::value>::type* = nullptr> + const typename U::value_type& at(const K& key) const { + return at(key, hash_key(key)); + } + + template <class K, class U = ValueSelect, + typename std::enable_if<has_mapped_type<U>::value>::type* = nullptr> + const typename U::value_type& at(const K& key, std::size_t hash) const { + auto it = find(key, hash); + if (it != cend()) { + return it.value(); + } else { + TSL_RH_THROW_OR_TERMINATE(std::out_of_range, "Couldn't find key."); + } + } + + template <class K, class U = ValueSelect, + typename std::enable_if<has_mapped_type<U>::value>::type* = nullptr> + typename U::value_type& operator[](K&& key) { + return try_emplace(std::forward<K>(key)).first.value(); + } + + template <class K> + size_type count(const K& key) const { + return count(key, hash_key(key)); + } + + template <class K> + size_type count(const K& key, std::size_t hash) const { + if (find(key, hash) != cend()) { + return 1; + } else { + return 0; + } + } + + template <class K> + iterator find(const K& key) { + return find_impl(key, hash_key(key)); + } + + template <class K> + iterator find(const K& key, std::size_t hash) { + return find_impl(key, hash); + } + + template <class K> + const_iterator find(const K& key) const { + return find_impl(key, hash_key(key)); + } + + template <class K> + const_iterator find(const K& key, std::size_t hash) const { + return find_impl(key, hash); + } + + template <class K> + bool contains(const K& key) const { + return contains(key, hash_key(key)); + } + + template <class K> + bool contains(const K& key, std::size_t hash) const { + return count(key, hash) != 0; + } + + template <class K> + std::pair<iterator, iterator> equal_range(const K& key) { + return equal_range(key, hash_key(key)); + } + + template <class K> + std::pair<iterator, iterator> equal_range(const K& key, std::size_t hash) { + iterator it = find(key, hash); + return std::make_pair(it, (it == end()) ? it : std::next(it)); + } + + template <class K> + std::pair<const_iterator, const_iterator> equal_range(const K& key) const { + return equal_range(key, hash_key(key)); + } + + template <class K> + std::pair<const_iterator, const_iterator> equal_range( + const K& key, std::size_t hash) const { + const_iterator it = find(key, hash); + return std::make_pair(it, (it == cend()) ? it : std::next(it)); + } + + /* + * Bucket interface + */ + size_type bucket_count() const { return m_bucket_count; } + + size_type max_bucket_count() const { + return std::min(GrowthPolicy::max_bucket_count(), + m_buckets_data.max_size()); + } + + /* + * Hash policy + */ + float load_factor() const { + if (bucket_count() == 0) { + return 0; + } + + return float(m_nb_elements) / float(bucket_count()); + } + + float min_load_factor() const { return m_min_load_factor; } + + float max_load_factor() const { return m_max_load_factor; } + + void min_load_factor(float ml) { + m_min_load_factor = clamp(ml, float(MINIMUM_MIN_LOAD_FACTOR), + float(MAXIMUM_MIN_LOAD_FACTOR)); + } + + void max_load_factor(float ml) { + m_max_load_factor = clamp(ml, float(MINIMUM_MAX_LOAD_FACTOR), + float(MAXIMUM_MAX_LOAD_FACTOR)); + m_load_threshold = size_type(float(bucket_count()) * m_max_load_factor); + tsl_rh_assert(bucket_count() == 0 || m_load_threshold < bucket_count()); + } + + void rehash(size_type count_) { + count_ = std::max(count_, + size_type(std::ceil(float(size()) / max_load_factor()))); + rehash_impl(count_); + } + + void reserve(size_type count_) { + rehash(size_type(std::ceil(float(count_) / max_load_factor()))); + } + + /* + * Observers + */ + hasher hash_function() const { return static_cast<const Hash&>(*this); } + + key_equal key_eq() const { return static_cast<const KeyEqual&>(*this); } + + /* + * Other + */ + iterator mutable_iterator(const_iterator pos) { + return iterator(const_cast<bucket_entry*>(pos.m_bucket)); + } + + template <class Serializer> + void serialize(Serializer& serializer) const { + serialize_impl(serializer); + } + + template <class Deserializer> + void deserialize(Deserializer& deserializer, bool hash_compatible) { + deserialize_impl(deserializer, hash_compatible); + } + + private: + template <class K> + std::size_t hash_key(const K& key) const { + return Hash::operator()(key); + } + + template <class K1, class K2> + bool compare_keys(const K1& key1, const K2& key2) const { + return KeyEqual::operator()(key1, key2); + } + + std::size_t bucket_for_hash(std::size_t hash) const { + const std::size_t bucket = GrowthPolicy::bucket_for_hash(hash); + tsl_rh_assert(bucket < m_bucket_count || + (bucket == 0 && m_bucket_count == 0)); + + return bucket; + } + + template <class U = GrowthPolicy, + typename std::enable_if<is_power_of_two_policy<U>::value>::type* = + nullptr> + std::size_t next_bucket(std::size_t index) const noexcept { + tsl_rh_assert(index < bucket_count()); + + return (index + 1) & this->m_mask; + } + + template <class U = GrowthPolicy, + typename std::enable_if<!is_power_of_two_policy<U>::value>::type* = + nullptr> + std::size_t next_bucket(std::size_t index) const noexcept { + tsl_rh_assert(index < bucket_count()); + + index++; + return (index != bucket_count()) ? index : 0; + } + + template <class K> + iterator find_impl(const K& key, std::size_t hash) { + return mutable_iterator( + static_cast<const robin_hash*>(this)->find(key, hash)); + } + + template <class K> + const_iterator find_impl(const K& key, std::size_t hash) const { + std::size_t ibucket = bucket_for_hash(hash); + distance_type dist_from_ideal_bucket = 0; + + while (dist_from_ideal_bucket <= + m_buckets[ibucket].dist_from_ideal_bucket()) { + if (TSL_RH_LIKELY( + (!USE_STORED_HASH_ON_LOOKUP || + m_buckets[ibucket].bucket_hash_equal(hash)) && + compare_keys(KeySelect()(m_buckets[ibucket].value()), key))) { + return const_iterator(m_buckets + ibucket); + } + + ibucket = next_bucket(ibucket); + dist_from_ideal_bucket++; + } + + return cend(); + } + + void erase_from_bucket(iterator pos) { + pos.m_bucket->clear(); + m_nb_elements--; + + /** + * Backward shift, swap the empty bucket, previous_ibucket, with the values + * on its right, ibucket, until we cross another empty bucket or if the + * other bucket has a distance_from_ideal_bucket == 0. + * + * We try to move the values closer to their ideal bucket. + */ + std::size_t previous_ibucket = + static_cast<std::size_t>(pos.m_bucket - m_buckets); + std::size_t ibucket = next_bucket(previous_ibucket); + + while (m_buckets[ibucket].dist_from_ideal_bucket() > 0) { + tsl_rh_assert(m_buckets[previous_ibucket].empty()); + + const distance_type new_distance = + distance_type(m_buckets[ibucket].dist_from_ideal_bucket() - 1); + m_buckets[previous_ibucket].set_value_of_empty_bucket( + new_distance, m_buckets[ibucket].truncated_hash(), + std::move(m_buckets[ibucket].value())); + m_buckets[ibucket].clear(); + + previous_ibucket = ibucket; + ibucket = next_bucket(ibucket); + } + } + + template <class K, class... Args> + std::pair<iterator, bool> insert_impl(const K& key, + Args&&... value_type_args) { + const std::size_t hash = hash_key(key); + + std::size_t ibucket = bucket_for_hash(hash); + distance_type dist_from_ideal_bucket = 0; + + while (dist_from_ideal_bucket <= + m_buckets[ibucket].dist_from_ideal_bucket()) { + if ((!USE_STORED_HASH_ON_LOOKUP || + m_buckets[ibucket].bucket_hash_equal(hash)) && + compare_keys(KeySelect()(m_buckets[ibucket].value()), key)) { + return std::make_pair(iterator(m_buckets + ibucket), false); + } + + ibucket = next_bucket(ibucket); + dist_from_ideal_bucket++; + } + + if (rehash_on_extreme_load()) { + ibucket = bucket_for_hash(hash); + dist_from_ideal_bucket = 0; + + while (dist_from_ideal_bucket <= + m_buckets[ibucket].dist_from_ideal_bucket()) { + ibucket = next_bucket(ibucket); + dist_from_ideal_bucket++; + } + } + + if (m_buckets[ibucket].empty()) { + m_buckets[ibucket].set_value_of_empty_bucket( + dist_from_ideal_bucket, bucket_entry::truncate_hash(hash), + std::forward<Args>(value_type_args)...); + } else { + insert_value(ibucket, dist_from_ideal_bucket, + bucket_entry::truncate_hash(hash), + std::forward<Args>(value_type_args)...); + } + + m_nb_elements++; + /* + * The value will be inserted in ibucket in any case, either because it was + * empty or by stealing the bucket (robin hood). + */ + return std::make_pair(iterator(m_buckets + ibucket), true); + } + + template <class... Args> + void insert_value(std::size_t ibucket, distance_type dist_from_ideal_bucket, + truncated_hash_type hash, Args&&... value_type_args) { + value_type value(std::forward<Args>(value_type_args)...); + insert_value_impl(ibucket, dist_from_ideal_bucket, hash, value); + } + + void insert_value(std::size_t ibucket, distance_type dist_from_ideal_bucket, + truncated_hash_type hash, value_type&& value) { + insert_value_impl(ibucket, dist_from_ideal_bucket, hash, value); + } + + /* + * We don't use `value_type&& value` as last argument due to a bug in MSVC + * when `value_type` is a pointer, The compiler is not able to see the + * difference between `std::string*` and `std::string*&&` resulting in a + * compilation error. + * + * The `value` will be in a moved state at the end of the function. + */ + void insert_value_impl(std::size_t ibucket, + distance_type dist_from_ideal_bucket, + truncated_hash_type hash, value_type& value) { + tsl_rh_assert(dist_from_ideal_bucket > + m_buckets[ibucket].dist_from_ideal_bucket()); + m_buckets[ibucket].swap_with_value_in_bucket(dist_from_ideal_bucket, hash, + value); + ibucket = next_bucket(ibucket); + dist_from_ideal_bucket++; + + while (!m_buckets[ibucket].empty()) { + if (dist_from_ideal_bucket > + m_buckets[ibucket].dist_from_ideal_bucket()) { + if (dist_from_ideal_bucket >= + bucket_entry::DIST_FROM_IDEAL_BUCKET_LIMIT) { + /** + * The number of probes is really high, rehash the map on the next + * insert. Difficult to do now as rehash may throw an exception. + */ + m_grow_on_next_insert = true; + } + + m_buckets[ibucket].swap_with_value_in_bucket(dist_from_ideal_bucket, + hash, value); + } + + ibucket = next_bucket(ibucket); + dist_from_ideal_bucket++; + } + + m_buckets[ibucket].set_value_of_empty_bucket(dist_from_ideal_bucket, hash, + std::move(value)); + } + + void rehash_impl(size_type count_) { + robin_hash new_table(count_, static_cast<Hash&>(*this), + static_cast<KeyEqual&>(*this), get_allocator(), + m_min_load_factor, m_max_load_factor); + tsl_rh_assert(size() <= new_table.m_load_threshold); + + const bool use_stored_hash = + USE_STORED_HASH_ON_REHASH(new_table.bucket_count()); + for (auto& bucket : m_buckets_data) { + if (bucket.empty()) { + continue; + } + + const std::size_t hash = + use_stored_hash ? bucket.truncated_hash() + : new_table.hash_key(KeySelect()(bucket.value())); + + new_table.insert_value_on_rehash(new_table.bucket_for_hash(hash), 0, + bucket_entry::truncate_hash(hash), + std::move(bucket.value())); + } + + new_table.m_nb_elements = m_nb_elements; + new_table.swap(*this); + } + + void clear_and_shrink() noexcept { + GrowthPolicy::clear(); + m_buckets_data.clear(); + m_buckets = static_empty_bucket_ptr(); + m_bucket_count = 0; + m_nb_elements = 0; + m_load_threshold = 0; + m_grow_on_next_insert = false; + m_try_shrink_on_next_insert = false; + } + + void insert_value_on_rehash(std::size_t ibucket, + distance_type dist_from_ideal_bucket, + truncated_hash_type hash, value_type&& value) { + while (true) { + if (dist_from_ideal_bucket > + m_buckets[ibucket].dist_from_ideal_bucket()) { + if (m_buckets[ibucket].empty()) { + m_buckets[ibucket].set_value_of_empty_bucket(dist_from_ideal_bucket, + hash, std::move(value)); + return; + } else { + m_buckets[ibucket].swap_with_value_in_bucket(dist_from_ideal_bucket, + hash, value); + } + } + + dist_from_ideal_bucket++; + ibucket = next_bucket(ibucket); + } + } + + /** + * Grow the table if m_grow_on_next_insert is true or we reached the + * max_load_factor. Shrink the table if m_try_shrink_on_next_insert is true + * (an erase occurred) and we're below the min_load_factor. + * + * Return true if the table has been rehashed. + */ + bool rehash_on_extreme_load() { + if (m_grow_on_next_insert || size() >= m_load_threshold) { + rehash_impl(GrowthPolicy::next_bucket_count()); + m_grow_on_next_insert = false; + + return true; + } + + if (m_try_shrink_on_next_insert) { + m_try_shrink_on_next_insert = false; + if (m_min_load_factor != 0.0f && load_factor() < m_min_load_factor) { + reserve(size() + 1); + + return true; + } + } + + return false; + } + + template <class Serializer> + void serialize_impl(Serializer& serializer) const { + const slz_size_type version = SERIALIZATION_PROTOCOL_VERSION; + serializer(version); + + // Indicate if the truncated hash of each bucket is stored. Use a + // std::int16_t instead of a bool to avoid the need for the serializer to + // support an extra 'bool' type. + const std::int16_t hash_stored_for_bucket = + static_cast<std::int16_t>(STORE_HASH); + serializer(hash_stored_for_bucket); + + const slz_size_type nb_elements = m_nb_elements; + serializer(nb_elements); + + const slz_size_type bucket_count = m_buckets_data.size(); + serializer(bucket_count); + + const float min_load_factor = m_min_load_factor; + serializer(min_load_factor); + + const float max_load_factor = m_max_load_factor; + serializer(max_load_factor); + + for (const bucket_entry& bucket : m_buckets_data) { + if (bucket.empty()) { + const std::int16_t empty_bucket = + bucket_entry::EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET; + serializer(empty_bucket); + } else { + const std::int16_t dist_from_ideal_bucket = + bucket.dist_from_ideal_bucket(); + serializer(dist_from_ideal_bucket); + if (STORE_HASH) { + const std::uint32_t truncated_hash = bucket.truncated_hash(); + serializer(truncated_hash); + } + serializer(bucket.value()); + } + } + } + + template <class Deserializer> + void deserialize_impl(Deserializer& deserializer, bool hash_compatible) { + tsl_rh_assert(m_buckets_data.empty()); // Current hash table must be empty + + const slz_size_type version = + deserialize_value<slz_size_type>(deserializer); + // For now we only have one version of the serialization protocol. + // If it doesn't match there is a problem with the file. + if (version != SERIALIZATION_PROTOCOL_VERSION) { + TSL_RH_THROW_OR_TERMINATE(std::runtime_error, + "Can't deserialize the ordered_map/set. " + "The protocol version header is invalid."); + } + + const bool hash_stored_for_bucket = + deserialize_value<std::int16_t>(deserializer) ? true : false; + if (hash_compatible && STORE_HASH != hash_stored_for_bucket) { + TSL_RH_THROW_OR_TERMINATE( + std::runtime_error, + "Can't deserialize a map with a different StoreHash " + "than the one used during the serialization when " + "hash compatibility is used"); + } + + const slz_size_type nb_elements = + deserialize_value<slz_size_type>(deserializer); + const slz_size_type bucket_count_ds = + deserialize_value<slz_size_type>(deserializer); + const float min_load_factor = deserialize_value<float>(deserializer); + const float max_load_factor = deserialize_value<float>(deserializer); + + if (min_load_factor < MINIMUM_MIN_LOAD_FACTOR || + min_load_factor > MAXIMUM_MIN_LOAD_FACTOR) { + TSL_RH_THROW_OR_TERMINATE( + std::runtime_error, + "Invalid min_load_factor. Check that the serializer " + "and deserializer support floats correctly as they " + "can be converted implicitly to ints."); + } + + if (max_load_factor < MINIMUM_MAX_LOAD_FACTOR || + max_load_factor > MAXIMUM_MAX_LOAD_FACTOR) { + TSL_RH_THROW_OR_TERMINATE( + std::runtime_error, + "Invalid max_load_factor. Check that the serializer " + "and deserializer support floats correctly as they " + "can be converted implicitly to ints."); + } + + this->min_load_factor(min_load_factor); + this->max_load_factor(max_load_factor); + + if (bucket_count_ds == 0) { + tsl_rh_assert(nb_elements == 0); + return; + } + + if (!hash_compatible) { + reserve(numeric_cast<size_type>(nb_elements, + "Deserialized nb_elements is too big.")); + for (slz_size_type ibucket = 0; ibucket < bucket_count_ds; ibucket++) { + const distance_type dist_from_ideal_bucket = + deserialize_value<std::int16_t>(deserializer); + if (dist_from_ideal_bucket != + bucket_entry::EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET) { + if (hash_stored_for_bucket) { + TSL_RH_UNUSED(deserialize_value<std::uint32_t>(deserializer)); + } + + insert(deserialize_value<value_type>(deserializer)); + } + } + + tsl_rh_assert(nb_elements == size()); + } else { + m_bucket_count = numeric_cast<size_type>( + bucket_count_ds, "Deserialized bucket_count is too big."); + + GrowthPolicy::operator=(GrowthPolicy(m_bucket_count)); + // GrowthPolicy should not modify the bucket count we got from + // deserialization + if (m_bucket_count != bucket_count_ds) { + TSL_RH_THROW_OR_TERMINATE(std::runtime_error, + "The GrowthPolicy is not the same even " + "though hash_compatible is true."); + } + + m_nb_elements = numeric_cast<size_type>( + nb_elements, "Deserialized nb_elements is too big."); + m_buckets_data.resize(m_bucket_count); + m_buckets = m_buckets_data.data(); + + for (bucket_entry& bucket : m_buckets_data) { + const distance_type dist_from_ideal_bucket = + deserialize_value<std::int16_t>(deserializer); + if (dist_from_ideal_bucket != + bucket_entry::EMPTY_MARKER_DIST_FROM_IDEAL_BUCKET) { + truncated_hash_type truncated_hash = 0; + if (hash_stored_for_bucket) { + tsl_rh_assert(hash_stored_for_bucket); + truncated_hash = deserialize_value<std::uint32_t>(deserializer); + } + + bucket.set_value_of_empty_bucket( + dist_from_ideal_bucket, truncated_hash, + deserialize_value<value_type>(deserializer)); + } + } + + if (!m_buckets_data.empty()) { + m_buckets_data.back().set_as_last_bucket(); + } + } + } + + public: + static const size_type DEFAULT_INIT_BUCKETS_SIZE = 0; + + static constexpr float DEFAULT_MAX_LOAD_FACTOR = 0.5f; + static constexpr float MINIMUM_MAX_LOAD_FACTOR = 0.2f; + static constexpr float MAXIMUM_MAX_LOAD_FACTOR = 0.95f; + + static constexpr float DEFAULT_MIN_LOAD_FACTOR = 0.0f; + static constexpr float MINIMUM_MIN_LOAD_FACTOR = 0.0f; + static constexpr float MAXIMUM_MIN_LOAD_FACTOR = 0.15f; + + static_assert(MINIMUM_MAX_LOAD_FACTOR < MAXIMUM_MAX_LOAD_FACTOR, + "MINIMUM_MAX_LOAD_FACTOR should be < MAXIMUM_MAX_LOAD_FACTOR"); + static_assert(MINIMUM_MIN_LOAD_FACTOR < MAXIMUM_MIN_LOAD_FACTOR, + "MINIMUM_MIN_LOAD_FACTOR should be < MAXIMUM_MIN_LOAD_FACTOR"); + static_assert(MAXIMUM_MIN_LOAD_FACTOR < MINIMUM_MAX_LOAD_FACTOR, + "MAXIMUM_MIN_LOAD_FACTOR should be < MINIMUM_MAX_LOAD_FACTOR"); + + private: + /** + * Protocol version currenlty used for serialization. + */ + static const slz_size_type SERIALIZATION_PROTOCOL_VERSION = 1; + + /** + * Return an always valid pointer to an static empty bucket_entry with + * last_bucket() == true. + */ + bucket_entry* static_empty_bucket_ptr() noexcept { + static bucket_entry empty_bucket(true); + tsl_rh_assert(empty_bucket.empty()); + return &empty_bucket; + } + + private: + buckets_container_type m_buckets_data; + + /** + * Points to m_buckets_data.data() if !m_buckets_data.empty() otherwise points + * to static_empty_bucket_ptr. This variable is useful to avoid the cost of + * checking if m_buckets_data is empty when trying to find an element. + * + * TODO Remove m_buckets_data and only use a pointer instead of a + * pointer+vector to save some space in the robin_hash object. Manage the + * Allocator manually. + */ + bucket_entry* m_buckets; + + /** + * Used a lot in find, avoid the call to m_buckets_data.size() which is a bit + * slower. + */ + size_type m_bucket_count; + + size_type m_nb_elements; + + size_type m_load_threshold; + + float m_min_load_factor; + float m_max_load_factor; + + bool m_grow_on_next_insert; + + /** + * We can't shrink down the map on erase operations as the erase methods need + * to return the next iterator. Shrinking the map would invalidate all the + * iterators and we could not return the next iterator in a meaningful way, On + * erase, we thus just indicate on erase that we should try to shrink the hash + * table on the next insert if we go below the min_load_factor. + */ + bool m_try_shrink_on_next_insert; +}; + +} // namespace detail_robin_hash + +} // namespace tsl + +#endif diff --git a/misc/benchmarks/external/tsl/robin_map.h b/misc/benchmarks/external/tsl/robin_map.h new file mode 100644 index 00000000..aeb354c3 --- /dev/null +++ b/misc/benchmarks/external/tsl/robin_map.h @@ -0,0 +1,807 @@ +/** + * MIT License + * + * Copyright (c) 2017 Thibaut Goetghebuer-Planchon <[email protected]> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef TSL_ROBIN_MAP_H +#define TSL_ROBIN_MAP_H + +#include <cstddef> +#include <functional> +#include <initializer_list> +#include <memory> +#include <type_traits> +#include <utility> + +#include "robin_hash.h" + +namespace tsl { + +/** + * Implementation of a hash map using open-addressing and the robin hood hashing + * algorithm with backward shift deletion. + * + * For operations modifying the hash map (insert, erase, rehash, ...), the + * strong exception guarantee is only guaranteed when the expression + * `std::is_nothrow_swappable<std::pair<Key, T>>::value && + * std::is_nothrow_move_constructible<std::pair<Key, T>>::value` is true, + * otherwise if an exception is thrown during the swap or the move, the hash map + * may end up in a undefined state. Per the standard a `Key` or `T` with a + * noexcept copy constructor and no move constructor also satisfies the + * `std::is_nothrow_move_constructible<std::pair<Key, T>>::value` criterion (and + * will thus guarantee the strong exception for the map). + * + * When `StoreHash` is true, 32 bits of the hash are stored alongside the + * values. It can improve the performance during lookups if the `KeyEqual` + * function takes time (if it engenders a cache-miss for example) as we then + * compare the stored hashes before comparing the keys. When + * `tsl::rh::power_of_two_growth_policy` is used as `GrowthPolicy`, it may also + * speed-up the rehash process as we can avoid to recalculate the hash. When it + * is detected that storing the hash will not incur any memory penalty due to + * alignment (i.e. `sizeof(tsl::detail_robin_hash::bucket_entry<ValueType, + * true>) == sizeof(tsl::detail_robin_hash::bucket_entry<ValueType, false>)`) + * and `tsl::rh::power_of_two_growth_policy` is used, the hash will be stored + * even if `StoreHash` is false so that we can speed-up the rehash (but it will + * not be used on lookups unless `StoreHash` is true). + * + * `GrowthPolicy` defines how the map grows and consequently how a hash value is + * mapped to a bucket. By default the map uses + * `tsl::rh::power_of_two_growth_policy`. This policy keeps the number of + * buckets to a power of two and uses a mask to map the hash to a bucket instead + * of the slow modulo. Other growth policies are available and you may define + * your own growth policy, check `tsl::rh::power_of_two_growth_policy` for the + * interface. + * + * `std::pair<Key, T>` must be swappable. + * + * `Key` and `T` must be copy and/or move constructible. + * + * If the destructor of `Key` or `T` throws an exception, the behaviour of the + * class is undefined. + * + * Iterators invalidation: + * - clear, operator=, reserve, rehash: always invalidate the iterators. + * - insert, emplace, emplace_hint, operator[]: if there is an effective + * insert, invalidate the iterators. + * - erase: always invalidate the iterators. + */ +template <class Key, class T, class Hash = std::hash<Key>, + class KeyEqual = std::equal_to<Key>, + class Allocator = std::allocator<std::pair<Key, T>>, + bool StoreHash = false, + class GrowthPolicy = tsl::rh::power_of_two_growth_policy<2>> +class robin_map { + private: + template <typename U> + using has_is_transparent = tsl::detail_robin_hash::has_is_transparent<U>; + + class KeySelect { + public: + using key_type = Key; + + const key_type& operator()( + const std::pair<Key, T>& key_value) const noexcept { + return key_value.first; + } + + key_type& operator()(std::pair<Key, T>& key_value) noexcept { + return key_value.first; + } + }; + + class ValueSelect { + public: + using value_type = T; + + const value_type& operator()( + const std::pair<Key, T>& key_value) const noexcept { + return key_value.second; + } + + value_type& operator()(std::pair<Key, T>& key_value) noexcept { + return key_value.second; + } + }; + + using ht = detail_robin_hash::robin_hash<std::pair<Key, T>, KeySelect, + ValueSelect, Hash, KeyEqual, + Allocator, StoreHash, GrowthPolicy>; + + public: + using key_type = typename ht::key_type; + using mapped_type = T; + using value_type = typename ht::value_type; + using size_type = typename ht::size_type; + using difference_type = typename ht::difference_type; + using hasher = typename ht::hasher; + using key_equal = typename ht::key_equal; + using allocator_type = typename ht::allocator_type; + using reference = typename ht::reference; + using const_reference = typename ht::const_reference; + using pointer = typename ht::pointer; + using const_pointer = typename ht::const_pointer; + using iterator = typename ht::iterator; + using const_iterator = typename ht::const_iterator; + + public: + /* + * Constructors + */ + robin_map() : robin_map(ht::DEFAULT_INIT_BUCKETS_SIZE) {} + + explicit robin_map(size_type bucket_count, const Hash& hash = Hash(), + const KeyEqual& equal = KeyEqual(), + const Allocator& alloc = Allocator()) + : m_ht(bucket_count, hash, equal, alloc) {} + + robin_map(size_type bucket_count, const Allocator& alloc) + : robin_map(bucket_count, Hash(), KeyEqual(), alloc) {} + + robin_map(size_type bucket_count, const Hash& hash, const Allocator& alloc) + : robin_map(bucket_count, hash, KeyEqual(), alloc) {} + + explicit robin_map(const Allocator& alloc) + : robin_map(ht::DEFAULT_INIT_BUCKETS_SIZE, alloc) {} + + template <class InputIt> + robin_map(InputIt first, InputIt last, + size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE, + const Hash& hash = Hash(), const KeyEqual& equal = KeyEqual(), + const Allocator& alloc = Allocator()) + : robin_map(bucket_count, hash, equal, alloc) { + insert(first, last); + } + + template <class InputIt> + robin_map(InputIt first, InputIt last, size_type bucket_count, + const Allocator& alloc) + : robin_map(first, last, bucket_count, Hash(), KeyEqual(), alloc) {} + + template <class InputIt> + robin_map(InputIt first, InputIt last, size_type bucket_count, + const Hash& hash, const Allocator& alloc) + : robin_map(first, last, bucket_count, hash, KeyEqual(), alloc) {} + + robin_map(std::initializer_list<value_type> init, + size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE, + const Hash& hash = Hash(), const KeyEqual& equal = KeyEqual(), + const Allocator& alloc = Allocator()) + : robin_map(init.begin(), init.end(), bucket_count, hash, equal, alloc) {} + + robin_map(std::initializer_list<value_type> init, size_type bucket_count, + const Allocator& alloc) + : robin_map(init.begin(), init.end(), bucket_count, Hash(), KeyEqual(), + alloc) {} + + robin_map(std::initializer_list<value_type> init, size_type bucket_count, + const Hash& hash, const Allocator& alloc) + : robin_map(init.begin(), init.end(), bucket_count, hash, KeyEqual(), + alloc) {} + + robin_map& operator=(std::initializer_list<value_type> ilist) { + m_ht.clear(); + + m_ht.reserve(ilist.size()); + m_ht.insert(ilist.begin(), ilist.end()); + + return *this; + } + + allocator_type get_allocator() const { return m_ht.get_allocator(); } + + /* + * Iterators + */ + iterator begin() noexcept { return m_ht.begin(); } + const_iterator begin() const noexcept { return m_ht.begin(); } + const_iterator cbegin() const noexcept { return m_ht.cbegin(); } + + iterator end() noexcept { return m_ht.end(); } + const_iterator end() const noexcept { return m_ht.end(); } + const_iterator cend() const noexcept { return m_ht.cend(); } + + /* + * Capacity + */ + bool empty() const noexcept { return m_ht.empty(); } + size_type size() const noexcept { return m_ht.size(); } + size_type max_size() const noexcept { return m_ht.max_size(); } + + /* + * Modifiers + */ + void clear() noexcept { m_ht.clear(); } + + std::pair<iterator, bool> insert(const value_type& value) { + return m_ht.insert(value); + } + + template <class P, typename std::enable_if<std::is_constructible< + value_type, P&&>::value>::type* = nullptr> + std::pair<iterator, bool> insert(P&& value) { + return m_ht.emplace(std::forward<P>(value)); + } + + std::pair<iterator, bool> insert(value_type&& value) { + return m_ht.insert(std::move(value)); + } + + iterator insert(const_iterator hint, const value_type& value) { + return m_ht.insert_hint(hint, value); + } + + template <class P, typename std::enable_if<std::is_constructible< + value_type, P&&>::value>::type* = nullptr> + iterator insert(const_iterator hint, P&& value) { + return m_ht.emplace_hint(hint, std::forward<P>(value)); + } + + iterator insert(const_iterator hint, value_type&& value) { + return m_ht.insert_hint(hint, std::move(value)); + } + + template <class InputIt> + void insert(InputIt first, InputIt last) { + m_ht.insert(first, last); + } + + void insert(std::initializer_list<value_type> ilist) { + m_ht.insert(ilist.begin(), ilist.end()); + } + + template <class M> + std::pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj) { + return m_ht.insert_or_assign(k, std::forward<M>(obj)); + } + + template <class M> + std::pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj) { + return m_ht.insert_or_assign(std::move(k), std::forward<M>(obj)); + } + + template <class M> + iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj) { + return m_ht.insert_or_assign(hint, k, std::forward<M>(obj)); + } + + template <class M> + iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj) { + return m_ht.insert_or_assign(hint, std::move(k), std::forward<M>(obj)); + } + + /** + * Due to the way elements are stored, emplace will need to move or copy the + * key-value once. The method is equivalent to + * insert(value_type(std::forward<Args>(args)...)); + * + * Mainly here for compatibility with the std::unordered_map interface. + */ + template <class... Args> + std::pair<iterator, bool> emplace(Args&&... args) { + return m_ht.emplace(std::forward<Args>(args)...); + } + + /** + * Due to the way elements are stored, emplace_hint will need to move or copy + * the key-value once. The method is equivalent to insert(hint, + * value_type(std::forward<Args>(args)...)); + * + * Mainly here for compatibility with the std::unordered_map interface. + */ + template <class... Args> + iterator emplace_hint(const_iterator hint, Args&&... args) { + return m_ht.emplace_hint(hint, std::forward<Args>(args)...); + } + + template <class... Args> + std::pair<iterator, bool> try_emplace(const key_type& k, Args&&... args) { + return m_ht.try_emplace(k, std::forward<Args>(args)...); + } + + template <class... Args> + std::pair<iterator, bool> try_emplace(key_type&& k, Args&&... args) { + return m_ht.try_emplace(std::move(k), std::forward<Args>(args)...); + } + + template <class... Args> + iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args) { + return m_ht.try_emplace_hint(hint, k, std::forward<Args>(args)...); + } + + template <class... Args> + iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args) { + return m_ht.try_emplace_hint(hint, std::move(k), + std::forward<Args>(args)...); + } + + iterator erase(iterator pos) { return m_ht.erase(pos); } + iterator erase(const_iterator pos) { return m_ht.erase(pos); } + iterator erase(const_iterator first, const_iterator last) { + return m_ht.erase(first, last); + } + size_type erase(const key_type& key) { return m_ht.erase(key); } + + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key). Useful to speed-up + * the lookup to the value if you already have the hash. + */ + size_type erase(const key_type& key, std::size_t precalculated_hash) { + return m_ht.erase(key, precalculated_hash); + } + + /** + * This overload only participates in the overload resolution if the typedef + * KeyEqual::is_transparent exists. If so, K must be hashable and comparable + * to Key. + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> + size_type erase(const K& key) { + return m_ht.erase(key); + } + + /** + * @copydoc erase(const K& key) + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key). Useful to speed-up + * the lookup to the value if you already have the hash. + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> + size_type erase(const K& key, std::size_t precalculated_hash) { + return m_ht.erase(key, precalculated_hash); + } + + void swap(robin_map& other) { other.m_ht.swap(m_ht); } + + /* + * Lookup + */ + T& at(const Key& key) { return m_ht.at(key); } + + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key). Useful to speed-up + * the lookup if you already have the hash. + */ + T& at(const Key& key, std::size_t precalculated_hash) { + return m_ht.at(key, precalculated_hash); + } + + const T& at(const Key& key) const { return m_ht.at(key); } + + /** + * @copydoc at(const Key& key, std::size_t precalculated_hash) + */ + const T& at(const Key& key, std::size_t precalculated_hash) const { + return m_ht.at(key, precalculated_hash); + } + + /** + * This overload only participates in the overload resolution if the typedef + * KeyEqual::is_transparent exists. If so, K must be hashable and comparable + * to Key. + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> + T& at(const K& key) { + return m_ht.at(key); + } + + /** + * @copydoc at(const K& key) + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key). Useful to speed-up + * the lookup if you already have the hash. + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> + T& at(const K& key, std::size_t precalculated_hash) { + return m_ht.at(key, precalculated_hash); + } + + /** + * @copydoc at(const K& key) + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> + const T& at(const K& key) const { + return m_ht.at(key); + } + + /** + * @copydoc at(const K& key, std::size_t precalculated_hash) + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> + const T& at(const K& key, std::size_t precalculated_hash) const { + return m_ht.at(key, precalculated_hash); + } + + T& operator[](const Key& key) { return m_ht[key]; } + T& operator[](Key&& key) { return m_ht[std::move(key)]; } + + size_type count(const Key& key) const { return m_ht.count(key); } + + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key). Useful to speed-up + * the lookup if you already have the hash. + */ + size_type count(const Key& key, std::size_t precalculated_hash) const { + return m_ht.count(key, precalculated_hash); + } + + /** + * This overload only participates in the overload resolution if the typedef + * KeyEqual::is_transparent exists. If so, K must be hashable and comparable + * to Key. + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> + size_type count(const K& key) const { + return m_ht.count(key); + } + + /** + * @copydoc count(const K& key) const + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key). Useful to speed-up + * the lookup if you already have the hash. + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> + size_type count(const K& key, std::size_t precalculated_hash) const { + return m_ht.count(key, precalculated_hash); + } + + iterator find(const Key& key) { return m_ht.find(key); } + + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key). Useful to speed-up + * the lookup if you already have the hash. + */ + iterator find(const Key& key, std::size_t precalculated_hash) { + return m_ht.find(key, precalculated_hash); + } + + const_iterator find(const Key& key) const { return m_ht.find(key); } + + /** + * @copydoc find(const Key& key, std::size_t precalculated_hash) + */ + const_iterator find(const Key& key, std::size_t precalculated_hash) const { + return m_ht.find(key, precalculated_hash); + } + + /** + * This overload only participates in the overload resolution if the typedef + * KeyEqual::is_transparent exists. If so, K must be hashable and comparable + * to Key. + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> + iterator find(const K& key) { + return m_ht.find(key); + } + + /** + * @copydoc find(const K& key) + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key). Useful to speed-up + * the lookup if you already have the hash. + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> + iterator find(const K& key, std::size_t precalculated_hash) { + return m_ht.find(key, precalculated_hash); + } + + /** + * @copydoc find(const K& key) + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> + const_iterator find(const K& key) const { + return m_ht.find(key); + } + + /** + * @copydoc find(const K& key) + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key). Useful to speed-up + * the lookup if you already have the hash. + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> + const_iterator find(const K& key, std::size_t precalculated_hash) const { + return m_ht.find(key, precalculated_hash); + } + + bool contains(const Key& key) const { return m_ht.contains(key); } + + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key). Useful to speed-up + * the lookup if you already have the hash. + */ + bool contains(const Key& key, std::size_t precalculated_hash) const { + return m_ht.contains(key, precalculated_hash); + } + + /** + * This overload only participates in the overload resolution if the typedef + * KeyEqual::is_transparent exists. If so, K must be hashable and comparable + * to Key. + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> + bool contains(const K& key) const { + return m_ht.contains(key); + } + + /** + * @copydoc contains(const K& key) const + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key). Useful to speed-up + * the lookup if you already have the hash. + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> + bool contains(const K& key, std::size_t precalculated_hash) const { + return m_ht.contains(key, precalculated_hash); + } + + std::pair<iterator, iterator> equal_range(const Key& key) { + return m_ht.equal_range(key); + } + + /** + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key). Useful to speed-up + * the lookup if you already have the hash. + */ + std::pair<iterator, iterator> equal_range(const Key& key, + std::size_t precalculated_hash) { + return m_ht.equal_range(key, precalculated_hash); + } + + std::pair<const_iterator, const_iterator> equal_range(const Key& key) const { + return m_ht.equal_range(key); + } + + /** + * @copydoc equal_range(const Key& key, std::size_t precalculated_hash) + */ + std::pair<const_iterator, const_iterator> equal_range( + const Key& key, std::size_t precalculated_hash) const { + return m_ht.equal_range(key, precalculated_hash); + } + + /** + * This overload only participates in the overload resolution if the typedef + * KeyEqual::is_transparent exists. If so, K must be hashable and comparable + * to Key. + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> + std::pair<iterator, iterator> equal_range(const K& key) { + return m_ht.equal_range(key); + } + + /** + * @copydoc equal_range(const K& key) + * + * Use the hash value 'precalculated_hash' instead of hashing the key. The + * hash value should be the same as hash_function()(key). Useful to speed-up + * the lookup if you already have the hash. + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> + std::pair<iterator, iterator> equal_range(const K& key, + std::size_t precalculated_hash) { + return m_ht.equal_range(key, precalculated_hash); + } + + /** + * @copydoc equal_range(const K& key) + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> + std::pair<const_iterator, const_iterator> equal_range(const K& key) const { + return m_ht.equal_range(key); + } + + /** + * @copydoc equal_range(const K& key, std::size_t precalculated_hash) + */ + template < + class K, class KE = KeyEqual, + typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> + std::pair<const_iterator, const_iterator> equal_range( + const K& key, std::size_t precalculated_hash) const { + return m_ht.equal_range(key, precalculated_hash); + } + + /* + * Bucket interface + */ + size_type bucket_count() const { return m_ht.bucket_count(); } + size_type max_bucket_count() const { return m_ht.max_bucket_count(); } + + /* + * Hash policy + */ + float load_factor() const { return m_ht.load_factor(); } + + float min_load_factor() const { return m_ht.min_load_factor(); } + float max_load_factor() const { return m_ht.max_load_factor(); } + + /** + * Set the `min_load_factor` to `ml`. When the `load_factor` of the map goes + * below `min_load_factor` after some erase operations, the map will be + * shrunk when an insertion occurs. The erase method itself never shrinks + * the map. + * + * The default value of `min_load_factor` is 0.0f, the map never shrinks by + * default. + */ + void min_load_factor(float ml) { m_ht.min_load_factor(ml); } + void max_load_factor(float ml) { m_ht.max_load_factor(ml); } + + void rehash(size_type count_) { m_ht.rehash(count_); } + void reserve(size_type count_) { m_ht.reserve(count_); } + + /* + * Observers + */ + hasher hash_function() const { return m_ht.hash_function(); } + key_equal key_eq() const { return m_ht.key_eq(); } + + /* + * Other + */ + + /** + * Convert a const_iterator to an iterator. + */ + iterator mutable_iterator(const_iterator pos) { + return m_ht.mutable_iterator(pos); + } + + /** + * Serialize the map through the `serializer` parameter. + * + * The `serializer` parameter must be a function object that supports the + * following call: + * - `template<typename U> void operator()(const U& value);` where the types + * `std::int16_t`, `std::uint32_t`, `std::uint64_t`, `float` and + * `std::pair<Key, T>` must be supported for U. + * + * The implementation leaves binary compatibility (endianness, IEEE 754 for + * floats, ...) of the types it serializes in the hands of the `Serializer` + * function object if compatibility is required. + */ + template <class Serializer> + void serialize(Serializer& serializer) const { + m_ht.serialize(serializer); + } + + /** + * Deserialize a previously serialized map through the `deserializer` + * parameter. + * + * The `deserializer` parameter must be a function object that supports the + * following call: + * - `template<typename U> U operator()();` where the types `std::int16_t`, + * `std::uint32_t`, `std::uint64_t`, `float` and `std::pair<Key, T>` must be + * supported for U. + * + * If the deserialized hash map type is hash compatible with the serialized + * map, the deserialization process can be sped up by setting + * `hash_compatible` to true. To be hash compatible, the Hash, KeyEqual and + * GrowthPolicy must behave the same way than the ones used on the serialized + * map and the StoreHash must have the same value. The `std::size_t` must also + * be of the same size as the one on the platform used to serialize the map. + * If these criteria are not met, the behaviour is undefined with + * `hash_compatible` sets to true. + * + * The behaviour is undefined if the type `Key` and `T` of the `robin_map` are + * not the same as the types used during serialization. + * + * The implementation leaves binary compatibility (endianness, IEEE 754 for + * floats, size of int, ...) of the types it deserializes in the hands of the + * `Deserializer` function object if compatibility is required. + */ + template <class Deserializer> + static robin_map deserialize(Deserializer& deserializer, + bool hash_compatible = false) { + robin_map map(0); + map.m_ht.deserialize(deserializer, hash_compatible); + + return map; + } + + friend bool operator==(const robin_map& lhs, const robin_map& rhs) { + if (lhs.size() != rhs.size()) { + return false; + } + + for (const auto& element_lhs : lhs) { + const auto it_element_rhs = rhs.find(element_lhs.first); + if (it_element_rhs == rhs.cend() || + element_lhs.second != it_element_rhs->second) { + return false; + } + } + + return true; + } + + friend bool operator!=(const robin_map& lhs, const robin_map& rhs) { + return !operator==(lhs, rhs); + } + + friend void swap(robin_map& lhs, robin_map& rhs) { lhs.swap(rhs); } + + private: + ht m_ht; +}; + +/** + * Same as `tsl::robin_map<Key, T, Hash, KeyEqual, Allocator, StoreHash, + * tsl::rh::prime_growth_policy>`. + */ +template <class Key, class T, class Hash = std::hash<Key>, + class KeyEqual = std::equal_to<Key>, + class Allocator = std::allocator<std::pair<Key, T>>, + bool StoreHash = false> +using robin_pg_map = robin_map<Key, T, Hash, KeyEqual, Allocator, StoreHash, + tsl::rh::prime_growth_policy>; + +} // end namespace tsl + +#endif diff --git a/misc/benchmarks/external/update.sh b/misc/benchmarks/external/update.sh new file mode 100644 index 00000000..45b472b1 --- /dev/null +++ b/misc/benchmarks/external/update.sh @@ -0,0 +1,38 @@ +tsl_h="https://raw.github.com/Tessil/hopscotch-map/master/include/tsl" +tsl_r="https://raw.github.com/Tessil/robin-map/master/include/tsl" +greg_s="https://raw.github.com/greg7mdp/sparsepp/master/sparsepp" +greg_p="https://raw.github.com/greg7mdp/parallel-hashmap" +martinus_r="https://raw.github.com/martinus/robin-hood-hashing/master/src/include" +martinus_d="https://raw.github.com/martinus/unordered_dense/master/include/ankerl" +skarupke="https://raw.github.com/skarupke/flat_hash_map/master" +ktprime="https://raw.github.com/ktprime/emhash/master" + +mkdir -p ankerl skarupke tsl emhash # sparsepp + +wget "$martinus_r/robin_hood.h" -O "ankerl/robin_hood.h" +wget "$martinus_d/unordered_dense.h" -O "ankerl/unordered_dense.h" + +wget "$skarupke/flat_hash_map.hpp" -O "skarupke/flat_hash_map.hpp" + +wget "$tsl_r/robin_growth_policy.h" -O "tsl/robin_growth_policy.h" +wget "$tsl_r/robin_hash.h" -O "tsl/robin_hash.h" +wget "$tsl_r/robin_map.h" -O "tsl/robin_map.h" + +#wget "$ktprime/thirdparty/wyhash.h" -O "emhash/wyhash.h" +wget "$ktprime/hash_table7.hpp" -O "emhash/hash_table7.hpp" + +#wget "$tsl_h/hopscotch_growth_policy.h" -O "tsl/hopscotch_growth_policy.h" +#wget "$tsl_h/hopscotch_hash.h" -O "tsl/hopscotch_hash.h" +#wget "$tsl_h/hopscotch_map.h" -O "tsl/hopscotch_map.h" + +#wget "$skarupke/bytell_hash_map.hpp" -O "skarupke/bytell_hash_map.hpp" + +#wget "$greg_s/spp.h" -O "sparsepp/spp.h" +#wget "$greg_s/spp_config.h" -O "sparsepp/spp_config.h" +#wget "$greg_s/spp_dlalloc.h" -O "sparsepp/spp_dlalloc.h" +#wget "$greg_s/spp_memory.h" -O "sparsepp/spp_memory.h" +#wget "$greg_s/spp_smartptr.h" -O "sparsepp/spp_smartptr.h" +#wget "$greg_s/spp_stdint.h" -O "sparsepp/spp_stdint.h" +#wget "$greg_s/spp_timer.h" -O "sparsepp/spp_timer.h" +#wget "$greg_s/spp_traits.h" -O "sparsepp/spp_traits.h" +#wget "$greg_s/spp_utils.h" -O "sparsepp/spp_utils.h" diff --git a/misc/benchmarks/misc/cbits_benchmark.cpp b/misc/benchmarks/misc/cbits_benchmark.cpp new file mode 100644 index 00000000..dd709db1 --- /dev/null +++ b/misc/benchmarks/misc/cbits_benchmark.cpp @@ -0,0 +1,123 @@ +#include <iostream> +#include <bitset> +#include <cstdlib> // rand +#include <ctime> // timer + +enum{ N=1<<22 }; // 4.2 mill. +#define i_static +#include <stc/crandom.h> +#define i_type cbits +#define i_len N +#include <stc/cbits.h> + +inline unsigned long get_time_in_ms() +{ + return (unsigned long)(1000 * clock() / CLOCKS_PER_SEC); +} + + +void one_sec_delay() +{ + unsigned long end_time = get_time_in_ms() + 1000; + + while(get_time_in_ms() < end_time) + { + } +} + + +int main(int argc, char **argv) +{ + size_t seed = time(NULL); + + using namespace std; + bool *bools = new bool[N]; + + unsigned long current_time, difference1, difference2; + uint64_t total; + + one_sec_delay(); + + total = 0; + csrandom(seed); + current_time = get_time_in_ms(); + + c_forrange (40 * N) + { + uint64_t r = crandom(); + bools[r & (N-1)] = r & 1<<29; + } + + difference1 = get_time_in_ms() - current_time; + current_time = get_time_in_ms(); + + c_forrange (100) c_forrange (num, N) + { + total += bools[num]; + } + + delete [] bools; + + difference2 = get_time_in_ms() - current_time; + + cout << "Bool:" << endl << "sum total = " << total << ", random access time = " << difference1 + << ", sequential access time = " << difference2 << endl << endl; + + one_sec_delay(); + + total = 0; + csrandom(seed); + current_time = get_time_in_ms(); + bitset<N> bits; + + c_forrange (40 * N) + { + uint64_t r = crandom(); + bits[r & (N-1)] = r & 1<<29; + } + + difference1 = get_time_in_ms() - current_time; + current_time = get_time_in_ms(); + + c_forrange (100) c_forrange (num, N) + { + total += bits[num]; + } + + difference2 = get_time_in_ms() - current_time; + + cout << "Bitset:" << endl << "sum total = " << total << ", random access time = " << difference1 + << ", sequential access time = " << difference2 << endl << endl; + + one_sec_delay(); + + total = 0; + csrandom(seed); + current_time = get_time_in_ms(); + cbits bits2 = cbits_with_size(N, false); + + c_forrange (40 * N) + { + uint64_t r = crandom(); + cbits_set_value(&bits2, r & (N-1), r & 1<<29); + } + + difference1 = get_time_in_ms() - current_time; + current_time = get_time_in_ms(); + + c_forrange (100) c_forrange (num, N) + { + total += cbits_at(&bits2, num); + } + + cbits_drop(&bits2); + + difference2 = get_time_in_ms() - current_time; + + cout << "cbits:" << endl << "sum total = " << total << ", random access time = " << difference1 + << ", sequential access time = " << difference2 << endl << endl; + + //cin.get(); + + return 0; +} diff --git a/misc/benchmarks/misc/names.txt b/misc/benchmarks/misc/names.txt new file mode 100644 index 00000000..561acbbf --- /dev/null +++ b/misc/benchmarks/misc/names.txt @@ -0,0 +1,5163 @@ +Mary +Patricia +Linda +Barbara +Elizabeth +Jennifer +Maria +Susan +Margaret +Dorothy +Lisa +Nancy +Karen +Betty +Helen +Sandra +Donna +Carol +Ruth +Sharon +Michelle +Laura +Sarah +Kimberly +Deborah +Jessica +Shirley +Cynthia +Angela +Melissa +Brenda +Amy +Anna +Rebecca +Virginia +Kathleen +Pamela +Martha +Debra +Amanda +Stephanie +Carolyn +Christine +Marie +Janet +Catherine +Frances +Ann +Joyce +Diane +Alice +Julie +Heather +Teresa +Doris +Gloria +Evelyn +Jean +Cheryl +Mildred +Katherine +Joan +Ashley +Judith +Rose +Janice +Kelly +Nicole +Judy +Christina +Kathy +Theresa +Beverly +Denise +Tammy +Irene +Jane +Lori +Rachel +Marilyn +Andrea +Kathryn +Louise +Sara +Anne +Jacqueline +Wanda +Bonnie +Julia +Ruby +Lois +Tina +Phyllis +Norma +Paula +Diana +Annie +Lillian +Emily +Robin +Peggy +Crystal +Gladys +Rita +Dawn +Connie +Florence +Tracy +Edna +Tiffany +Carmen +Rosa +Cindy +Grace +Wendy +Victoria +Edith +Kim +Sherry +Sylvia +Josephine +Thelma +Shannon +Sheila +Ethel +Ellen +Elaine +Marjorie +Carrie +Charlotte +Monica +Esther +Pauline +Emma +Juanita +Anita +Rhonda +Hazel +Amber +Eva +Debbie +April +Leslie +Clara +Lucille +Jamie +Joanne +Eleanor +Valerie +Danielle +Megan +Alicia +Suzanne +Michele +Gail +Bertha +Darlene +Veronica +Jill +Erin +Geraldine +Lauren +Cathy +Joann +Lorraine +Lynn +Sally +Regina +Erica +Beatrice +Dolores +Bernice +Audrey +Yvonne +Annette +June +Samantha +Marion +Dana +Stacy +Ana +Renee +Ida +Vivian +Roberta +Holly +Brittany +Melanie +Loretta +Yolanda +Jeanette +Laurie +Katie +Kristen +Vanessa +Alma +Sue +Elsie +Beth +Jeanne +Vicki +Carla +Tara +Rosemary +Eileen +Terri +Gertrude +Lucy +Tonya +Ella +Stacey +Wilma +Gina +Kristin +Jessie +Natalie +Agnes +Vera +Willie +Charlene +Bessie +Delores +Melinda +Pearl +Arlene +Maureen +Colleen +Allison +Tamara +Joy +Georgia +Constance +Lillie +Claudia +Jackie +Marcia +Tanya +Nellie +Minnie +Marlene +Heidi +Glenda +Lydia +Viola +Courtney +Marian +Stella +Caroline +Dora +Jo +Vickie +Mattie +Terry +Maxine +Irma +Mabel +Marsha +Myrtle +Lena +Christy +Deanna +Patsy +Hilda +Gwendolyn +Jennie +Nora +Margie +Nina +Cassandra +Leah +Penny +Kay +Priscilla +Naomi +Carole +Brandy +Olga +Billie +Dianne +Tracey +Leona +Jenny +Felicia +Sonia +Miriam +Velma +Becky +Bobbie +Violet +Kristina +Toni +Misty +Mae +Shelly +Daisy +Ramona +Sherri +Erika +Katrina +Claire +Lindsey +Lindsay +Geneva +Guadalupe +Belinda +Margarita +Sheryl +Cora +Faye +Ada +Natasha +Sabrina +Isabel +Marguerite +Hattie +Harriet +Molly +Cecilia +Kristi +Brandi +Blanche +Sandy +Rosie +Joanna +Iris +Eunice +Angie +Inez +Lynda +Madeline +Amelia +Alberta +Genevieve +Monique +Jodi +Janie +Maggie +Kayla +Sonya +Jan +Lee +Kristine +Candace +Fannie +Maryann +Opal +Alison +Yvette +Melody +Luz +Susie +Olivia +Flora +Shelley +Kristy +Mamie +Lula +Lola +Verna +Beulah +Antoinette +Candice +Juana +Jeannette +Pam +Kelli +Hannah +Whitney +Bridget +Karla +Celia +Latoya +Patty +Shelia +Gayle +Della +Vicky +Lynne +Sheri +Marianne +Kara +Jacquelyn +Erma +Blanca +Myra +Leticia +Pat +Krista +Roxanne +Angelica +Johnnie +Robyn +Francis +Adrienne +Rosalie +Alexandra +Brooke +Bethany +Sadie +Bernadette +Traci +Jody +Kendra +Jasmine +Nichole +Rachael +Chelsea +Mable +Ernestine +Muriel +Marcella +Elena +Krystal +Angelina +Nadine +Kari +Estelle +Dianna +Paulette +Lora +Mona +Doreen +Rosemarie +Angel +Desiree +Antonia +Hope +Ginger +Janis +Betsy +Christie +Freda +Mercedes +Meredith +Lynette +Teri +Cristina +Eula +Leigh +Meghan +Sophia +Eloise +Rochelle +Gretchen +Cecelia +Raquel +Henrietta +Alyssa +Jana +Kelley +Gwen +Kerry +Jenna +Tricia +Laverne +Olive +Alexis +Tasha +Silvia +Elvira +Casey +Delia +Sophie +Kate +Patti +Lorena +Kellie +Sonja +Lila +Lana +Darla +May +Mindy +Essie +Mandy +Lorene +Elsa +Josefina +Jeannie +Miranda +Dixie +Lucia +Marta +Faith +Lela +Johanna +Shari +Camille +Tami +Shawna +Elisa +Ebony +Melba +Ora +Nettie +Tabitha +Ollie +Jaime +Winifred +Kristie +Marina +Alisha +Aimee +Rena +Myrna +Marla +Tammie +Latasha +Bonita +Patrice +Ronda +Sherrie +Addie +Francine +Deloris +Stacie +Adriana +Cheri +Shelby +Abigail +Celeste +Jewel +Cara +Adele +Rebekah +Lucinda +Dorthy +Chris +Effie +Trina +Reba +Shawn +Sallie +Aurora +Lenora +Etta +Lottie +Kerri +Trisha +Nikki +Estella +Francisca +Josie +Tracie +Marissa +Karin +Brittney +Janelle +Lourdes +Laurel +Helene +Fern +Elva +Corinne +Kelsey +Ina +Bettie +Elisabeth +Aida +Caitlin +Ingrid +Iva +Eugenia +Christa +Goldie +Cassie +Maude +Jenifer +Therese +Frankie +Dena +Lorna +Janette +Latonya +Candy +Morgan +Consuelo +Tamika +Rosetta +Debora +Cherie +Polly +Dina +Jewell +Fay +Jillian +Dorothea +Nell +Trudy +Esperanza +Patrica +Kimberley +Shanna +Helena +Carolina +Cleo +Stefanie +Rosario +Ola +Janine +Mollie +Lupe +Alisa +Lou +Maribel +Susanne +Bette +Susana +Elise +Cecile +Isabelle +Lesley +Jocelyn +Paige +Joni +Rachelle +Leola +Daphne +Alta +Ester +Petra +Graciela +Imogene +Jolene +Keisha +Lacey +Glenna +Gabriela +Keri +Ursula +Lizzie +Kirsten +Shana +Adeline +Mayra +Jayne +Jaclyn +Gracie +Sondra +Carmela +Marisa +Rosalind +Charity +Tonia +Beatriz +Marisol +Clarice +Jeanine +Sheena +Angeline +Frieda +Lily +Robbie +Shauna +Millie +Claudette +Cathleen +Angelia +Gabrielle +Autumn +Katharine +Summer +Jodie +Staci +Lea +Christi +Jimmie +Justine +Elma +Luella +Margret +Dominique +Socorro +Rene +Martina +Margo +Mavis +Callie +Bobbi +Maritza +Lucile +Leanne +Jeannine +Deana +Aileen +Lorie +Ladonna +Willa +Manuela +Gale +Selma +Dolly +Sybil +Abby +Lara +Dale +Ivy +Dee +Winnie +Marcy +Luisa +Jeri +Magdalena +Ofelia +Meagan +Audra +Matilda +Leila +Cornelia +Bianca +Simone +Bettye +Randi +Virgie +Latisha +Barbra +Georgina +Eliza +Leann +Bridgette +Rhoda +Haley +Adela +Nola +Bernadine +Flossie +Ila +Greta +Ruthie +Nelda +Minerva +Lilly +Terrie +Letha +Hilary +Estela +Valarie +Brianna +Rosalyn +Earline +Catalina +Ava +Mia +Clarissa +Lidia +Corrine +Alexandria +Concepcion +Tia +Sharron +Rae +Dona +Ericka +Jami +Elnora +Chandra +Lenore +Neva +Marylou +Melisa +Tabatha +Serena +Avis +Allie +Sofia +Jeanie +Odessa +Nannie +Harriett +Loraine +Penelope +Milagros +Emilia +Benita +Allyson +Ashlee +Tania +Tommie +Esmeralda +Karina +Eve +Pearlie +Zelma +Malinda +Noreen +Tameka +Saundra +Hillary +Amie +Althea +Rosalinda +Jordan +Lilia +Alana +Gay +Clare +Alejandra +Elinor +Michael +Lorrie +Jerri +Darcy +Earnestine +Carmella +Taylor +Noemi +Marcie +Liza +Annabelle +Louisa +Earlene +Mallory +Carlene +Nita +Selena +Tanisha +Katy +Julianne +John +Lakisha +Edwina +Maricela +Margery +Kenya +Dollie +Roxie +Roslyn +Kathrine +Nanette +Charmaine +Lavonne +Ilene +Kris +Tammi +Suzette +Corine +Kaye +Jerry +Merle +Chrystal +Lina +Deanne +Lilian +Juliana +Aline +Luann +Kasey +Maryanne +Evangeline +Colette +Melva +Lawanda +Yesenia +Nadia +Madge +Kathie +Eddie +Ophelia +Valeria +Nona +Mitzi +Mari +Georgette +Claudine +Fran +Alissa +Roseann +Lakeisha +Susanna +Reva +Deidre +Chasity +Sheree +Carly +James +Elvia +Alyce +Deirdre +Gena +Briana +Araceli +Katelyn +Rosanne +Wendi +Tessa +Berta +Marva +Imelda +Marietta +Marci +Leonor +Arline +Sasha +Madelyn +Janna +Juliette +Deena +Aurelia +Josefa +Augusta +Liliana +Young +Christian +Lessie +Amalia +Savannah +Anastasia +Vilma +Natalia +Rosella +Lynnette +Corina +Alfreda +Leanna +Carey +Amparo +Coleen +Tamra +Aisha +Wilda +Karyn +Cherry +Queen +Maura +Mai +Evangelina +Rosanna +Hallie +Erna +Enid +Mariana +Lacy +Juliet +Jacklyn +Freida +Madeleine +Mara +Hester +Cathryn +Lelia +Casandra +Bridgett +Angelita +Jannie +Dionne +Annmarie +Katina +Beryl +Phoebe +Millicent +Katheryn +Diann +Carissa +Maryellen +Liz +Lauri +Helga +Gilda +Adrian +Rhea +Marquita +Hollie +Tisha +Tamera +Angelique +Francesca +Britney +Kaitlin +Lolita +Florine +Rowena +Reyna +Twila +Fanny +Janell +Ines +Concetta +Bertie +Alba +Brigitte +Alyson +Vonda +Pansy +Elba +Noelle +Letitia +Kitty +Deann +Brandie +Louella +Leta +Felecia +Sharlene +Lesa +Beverley +Robert +Isabella +Herminia +Terra +Celina +Tori +Octavia +Jade +Denice +Germaine +Sierra +Michell +Cortney +Nelly +Doretha +Sydney +Deidra +Monika +Lashonda +Judi +Chelsey +Antionette +Margot +Bobby +Adelaide +Nan +Leeann +Elisha +Dessie +Libby +Kathi +Gayla +Latanya +Mina +Mellisa +Kimberlee +Jasmin +Renae +Zelda +Elda +Ma +Justina +Gussie +Emilie +Camilla +Abbie +Rocio +Kaitlyn +Jesse +Edythe +Ashleigh +Selina +Lakesha +Geri +Allene +Pamala +Michaela +Dayna +Caryn +Rosalia +Sun +Jacquline +Rebeca +Marybeth +Krystle +Iola +Dottie +Bennie +Belle +Aubrey +Griselda +Ernestina +Elida +Adrianne +Demetria +Delma +Chong +Jaqueline +Destiny +Arleen +Virgina +Retha +Fatima +Tillie +Eleanore +Cari +Treva +Birdie +Wilhelmina +Rosalee +Maurine +Latrice +Yong +Jena +Taryn +Elia +Debby +Maudie +Jeanna +Delilah +Catrina +Shonda +Hortencia +Theodora +Teresita +Robbin +Danette +Maryjane +Freddie +Delphine +Brianne +Nilda +Danna +Cindi +Bess +Iona +Hanna +Ariel +Winona +Vida +Rosita +Marianna +William +Racheal +Guillermina +Eloisa +Celestine +Caren +Malissa +Lona +Chantel +Shellie +Marisela +Leora +Agatha +Soledad +Migdalia +Ivette +Christen +Athena +Janel +Chloe +Veda +Pattie +Tessie +Tera +Marilynn +Lucretia +Karrie +Dinah +Daniela +Alecia +Adelina +Vernice +Shiela +Portia +Merry +Lashawn +Devon +Dara +Tawana +Oma +Verda +Christin +Alene +Zella +Sandi +Rafaela +Maya +Kira +Candida +Alvina +Suzan +Shayla +Lyn +Lettie +Alva +Samatha +Oralia +Matilde +Madonna +Larissa +Vesta +Renita +India +Delois +Shanda +Phillis +Lorri +Erlinda +Cruz +Cathrine +Barb +Zoe +Isabell +Ione +Gisela +Charlie +Valencia +Roxanna +Mayme +Kisha +Ellie +Mellissa +Dorris +Dalia +Bella +Annetta +Zoila +Reta +Reina +Lauretta +Kylie +Christal +Pilar +Charla +Elissa +Tiffani +Tana +Paulina +Leota +Breanna +Jayme +Carmel +Vernell +Tomasa +Mandi +Dominga +Santa +Melodie +Lura +Alexa +Tamela +Ryan +Mirna +Kerrie +Venus +Noel +Felicita +Cristy +Carmelita +Berniece +Annemarie +Tiara +Roseanne +Missy +Cori +Roxana +Pricilla +Kristal +Jung +Elyse +Haydee +Aletha +Bettina +Marge +Gillian +Filomena +Charles +Zenaida +Harriette +Caridad +Vada +Una +Aretha +Pearline +Marjory +Marcela +Flor +Evette +Elouise +Alina +Trinidad +David +Damaris +Catharine +Carroll +Belva +Nakia +Marlena +Luanne +Lorine +Karon +Dorene +Danita +Brenna +Tatiana +Sammie +Louann +Loren +Julianna +Andria +Philomena +Lucila +Leonora +Dovie +Romona +Mimi +Jacquelin +Gaye +Tonja +Misti +Joe +Gene +Chastity +Stacia +Roxann +Micaela +Nikita +Mei +Velda +Marlys +Johnna +Aura +Lavern +Ivonne +Hayley +Nicki +Majorie +Herlinda +George +Alpha +Yadira +Perla +Gregoria +Daniel +Antonette +Shelli +Mozelle +Mariah +Joelle +Cordelia +Josette +Chiquita +Trista +Louis +Laquita +Georgiana +Candi +Shanon +Lonnie +Hildegard +Cecil +Valentina +Stephany +Magda +Karol +Gerry +Gabriella +Tiana +Roma +Richelle +Ray +Princess +Oleta +Jacque +Idella +Alaina +Suzanna +Jovita +Blair +Tosha +Raven +Nereida +Marlyn +Kyla +Joseph +Delfina +Tena +Stephenie +Sabina +Nathalie +Marcelle +Gertie +Darleen +Thea +Sharonda +Shantel +Belen +Venessa +Rosalina +Ona +Genoveva +Corey +Clementine +Rosalba +Renate +Renata +Mi +Ivory +Georgianna +Floy +Dorcas +Ariana +Tyra +Theda +Mariam +Juli +Jesica +Donnie +Vikki +Verla +Roselyn +Melvina +Jannette +Ginny +Debrah +Corrie +Asia +Violeta +Myrtis +Latricia +Collette +Charleen +Anissa +Viviana +Twyla +Precious +Nedra +Latonia +Lan +Hellen +Fabiola +Annamarie +Adell +Sharyn +Chantal +Niki +Maud +Lizette +Lindy +Kia +Kesha +Jeana +Danelle +Charline +Chanel +Carrol +Valorie +Lia +Dortha +Cristal +Sunny +Leone +Leilani +Gerri +Debi +Andra +Keshia +Ima +Eulalia +Easter +Dulce +Natividad +Linnie +Kami +Georgie +Catina +Brook +Alda +Winnifred +Sharla +Ruthann +Meaghan +Magdalene +Lissette +Adelaida +Venita +Trena +Shirlene +Shameka +Elizebeth +Dian +Shanta +Mickey +Latosha +Carlotta +Windy +Soon +Rosina +Mariann +Leisa +Jonnie +Dawna +Cathie +Billy +Astrid +Sidney +Laureen +Janeen +Holli +Fawn +Vickey +Teressa +Shante +Rubye +Marcelina +Chanda +Cary +Terese +Scarlett +Marty +Marnie +Lulu +Lisette +Jeniffer +Elenor +Dorinda +Donita +Carman +Bernita +Altagracia +Aleta +Adrianna +Zoraida +Ronnie +Nicola +Lyndsey +Kendall +Janina +Chrissy +Ami +Starla +Phylis +Phuong +Kyra +Charisse +Blanch +Sanjuanita +Rona +Nanci +Marilee +Maranda +Cory +Brigette +Sanjuana +Marita +Kassandra +Joycelyn +Ira +Felipa +Chelsie +Bonny +Mireya +Lorenza +Kyong +Ileana +Candelaria +Tony +Toby +Sherie +Ok +Mark +Lucie +Leatrice +Lakeshia +Gerda +Edie +Bambi +Marylin +Lavon +Hortense +Garnet +Evie +Tressa +Shayna +Lavina +Kyung +Jeanetta +Sherrill +Shara +Phyliss +Mittie +Anabel +Alesia +Thuy +Tawanda +Richard +Joanie +Tiffanie +Lashanda +Karissa +Enriqueta +Daria +Daniella +Corinna +Alanna +Abbey +Roxane +Roseanna +Magnolia +Lida +Kyle +Joellen +Era +Coral +Carleen +Tresa +Peggie +Novella +Nila +Maybelle +Jenelle +Carina +Nova +Melina +Marquerite +Margarette +Josephina +Evonne +Devin +Cinthia +Albina +Toya +Tawnya +Sherita +Santos +Myriam +Lizabeth +Lise +Keely +Jenni +Giselle +Cheryle +Ardith +Ardis +Alesha +Adriane +Shaina +Linnea +Karolyn +Hong +Florida +Felisha +Dori +Darci +Artie +Armida +Zola +Xiomara +Vergie +Shamika +Nena +Nannette +Maxie +Lovie +Jeane +Jaimie +Inge +Farrah +Elaina +Caitlyn +Starr +Felicitas +Cherly +Caryl +Yolonda +Yasmin +Teena +Prudence +Pennie +Nydia +Mackenzie +Orpha +Marvel +Lizbeth +Laurette +Jerrie +Hermelinda +Carolee +Tierra +Mirian +Meta +Melony +Kori +Jennette +Jamila +Ena +Anh +Yoshiko +Susannah +Salina +Rhiannon +Joleen +Cristine +Ashton +Aracely +Tomeka +Shalonda +Marti +Lacie +Kala +Jada +Ilse +Hailey +Brittani +Zona +Syble +Sherryl +Randy +Nidia +Marlo +Kandice +Kandi +Deb +Dean +America +Alycia +Tommy +Ronna +Norene +Mercy +Jose +Ingeborg +Giovanna +Gemma +Christel +Audry +Zora +Vita +Van +Trish +Stephaine +Shirlee +Shanika +Melonie +Mazie +Jazmin +Inga +Hoa +Hettie +Geralyn +Fonda +Estrella +Adella +Su +Sarita +Rina +Milissa +Maribeth +Golda +Evon +Ethelyn +Enedina +Cherise +Chana +Velva +Tawanna +Sade +Mirta +Li +Karie +Jacinta +Elna +Davina +Cierra +Ashlie +Albertha +Tanesha +Stephani +Nelle +Mindi +Lu +Lorinda +Larue +Florene +Demetra +Dedra +Ciara +Chantelle +Ashly +Suzy +Rosalva +Noelia +Lyda +Leatha +Krystyna +Kristan +Karri +Darline +Darcie +Cinda +Cheyenne +Cherrie +Awilda +Almeda +Rolanda +Lanette +Jerilyn +Gisele +Evalyn +Cyndi +Cleta +Carin +Zina +Zena +Velia +Tanika +Paul +Charissa +Thomas +Talia +Margarete +Lavonda +Kaylee +Kathlene +Jonna +Irena +Ilona +Idalia +Candis +Candance +Brandee +Anitra +Alida +Sigrid +Nicolette +Maryjo +Linette +Hedwig +Christiana +Cassidy +Alexia +Tressie +Modesta +Lupita +Lita +Gladis +Evelia +Davida +Cherri +Cecily +Ashely +Annabel +Agustina +Wanita +Shirly +Rosaura +Hulda +Eun +Bailey +Yetta +Verona +Thomasina +Sibyl +Shannan +Mechelle +Lue +Leandra +Lani +Kylee +Kandy +Jolynn +Ferne +Eboni +Corene +Alysia +Zula +Nada +Moira +Lyndsay +Lorretta +Juan +Jammie +Hortensia +Gaynell +Cameron +Adria +Vina +Vicenta +Tangela +Stephine +Norine +Nella +Liana +Leslee +Kimberely +Iliana +Glory +Felica +Emogene +Elfriede +Eden +Eartha +Carma +Bea +Ocie +Marry +Lennie +Kiara +Jacalyn +Carlota +Arielle +Yu +Star +Otilia +Kirstin +Kacey +Johnetta +Joey +Joetta +Jeraldine +Jaunita +Elana +Dorthea +Cami +Amada +Adelia +Vernita +Tamar +Siobhan +Renea +Rashida +Ouida +Odell +Nilsa +Meryl +Kristyn +Julieta +Danica +Breanne +Aurea +Anglea +Sherron +Odette +Malia +Lorelei +Lin +Leesa +Kenna +Kathlyn +Fiona +Charlette +Suzie +Shantell +Sabra +Racquel +Myong +Mira +Martine +Lucienne +Lavada +Juliann +Johnie +Elvera +Delphia +Clair +Christiane +Charolette +Carri +Augustine +Asha +Angella +Paola +Ninfa +Leda +Lai +Eda +Sunshine +Stefani +Shanell +Palma +Machelle +Lissa +Kecia +Kathryne +Karlene +Julissa +Jettie +Jenniffer +Hui +Corrina +Christopher +Carolann +Alena +Tess +Rosaria +Myrtice +Marylee +Liane +Kenyatta +Judie +Janey +In +Elmira +Eldora +Denna +Cristi +Cathi +Zaida +Vonnie +Viva +Vernie +Rosaline +Mariela +Luciana +Lesli +Karan +Felice +Deneen +Adina +Wynona +Tarsha +Sheron +Shasta +Shanita +Shani +Shandra +Randa +Pinkie +Paris +Nelida +Marilou +Lyla +Laurene +Laci +Joi +Janene +Dorotha +Daniele +Dani +Carolynn +Carlyn +Berenice +Ayesha +Anneliese +Alethea +Thersa +Tamiko +Rufina +Oliva +Mozell +Marylyn +Madison +Kristian +Kathyrn +Kasandra +Kandace +Janae +Gabriel +Domenica +Debbra +Dannielle +Chun +Buffy +Barbie +Arcelia +Aja +Zenobia +Sharen +Sharee +Patrick +Page +My +Lavinia +Kum +Kacie +Jackeline +Huong +Felisa +Emelia +Eleanora +Cythia +Cristin +Clyde +Claribel +Caron +Anastacia +Zulma +Zandra +Yoko +Tenisha +Susann +Sherilyn +Shay +Shawanda +Sabine +Romana +Mathilda +Linsey +Keiko +Joana +Isela +Gretta +Georgetta +Eugenie +Dusty +Desirae +Delora +Corazon +Antonina +Anika +Willene +Tracee +Tamatha +Regan +Nichelle +Mickie +Maegan +Luana +Lanita +Kelsie +Edelmira +Bree +Afton +Teodora +Tamie +Shena +Meg +Linh +Keli +Kaci +Danyelle +Britt +Arlette +Albertine +Adelle +Tiffiny +Stormy +Simona +Numbers +Nicolasa +Nichol +Nia +Nakisha +Mee +Maira +Loreen +Kizzy +Johnny +Jay +Fallon +Christene +Bobbye +Anthony +Ying +Vincenza +Tanja +Rubie +Roni +Queenie +Margarett +Kimberli +Irmgard +Idell +Hilma +Evelina +Esta +Emilee +Dennise +Dania +Carl +Carie +Antonio +Wai +Sang +Risa +Rikki +Particia +Mui +Masako +Mario +Luvenia +Loree +Loni +Lien +Kevin +Gigi +Florencia +Dorian +Denita +Dallas +Chi +Billye +Alexander +Tomika +Sharita +Rana +Nikole +Neoma +Margarite +Madalyn +Lucina +Laila +Kali +Jenette +Gabriele +Evelyne +Elenora +Clementina +Alejandrina +Zulema +Violette +Vannessa +Thresa +Retta +Pia +Patience +Noella +Nickie +Jonell +Delta +Chung +Chaya +Camelia +Bethel +Anya +Andrew +Thanh +Suzann +Spring +Shu +Mila +Lilla +Laverna +Keesha +Kattie +Gia +Georgene +Eveline +Estell +Elizbeth +Vivienne +Vallie +Trudie +Stephane +Michel +Magaly +Madie +Kenyetta +Karren +Janetta +Hermine +Harmony +Drucilla +Debbi +Celestina +Candie +Britni +Beckie +Amina +Zita +Yun +Yolande +Vivien +Vernetta +Trudi +Sommer +Pearle +Patrina +Ossie +Nicolle +Loyce +Letty +Larisa +Katharina +Joselyn +Jonelle +Jenell +Iesha +Heide +Florinda +Florentina +Flo +Elodia +Dorine +Brunilda +Brigid +Ashli +Ardella +Twana +Thu +Tarah +Sung +Shea +Shavon +Shane +Serina +Rayna +Ramonita +Nga +Margurite +Lucrecia +Kourtney +Kati +Jesus +Jesenia +Diamond +Crista +Ayana +Alica +Alia +Vinnie +Suellen +Romelia +Rachell +Piper +Olympia +Michiko +Kathaleen +Jolie +Jessi +Janessa +Hana +Ha +Elease +Carletta +Britany +Shona +Salome +Rosamond +Regena +Raina +Ngoc +Nelia +Louvenia +Lesia +Latrina +Laticia +Larhonda +Jina +Jacki +Hollis +Holley +Emmy +Deeann +Coretta +Arnetta +Velvet +Thalia +Shanice +Neta +Mikki +Micki +Lonna +Leana +Lashunda +Kiley +Joye +Jacqulyn +Ignacia +Hyun +Hiroko +Henry +Henriette +Elayne +Delinda +Darnell +Dahlia +Coreen +Consuela +Conchita +Celine +Babette +Ayanna +Anette +Albertina +Skye +Shawnee +Shaneka +Quiana +Pamelia +Min +Merri +Merlene +Margit +Kiesha +Kiera +Kaylene +Jodee +Jenise +Erlene +Emmie +Else +Daryl +Dalila +Daisey +Cody +Casie +Belia +Babara +Versie +Vanesa +Shelba +Shawnda +Sam +Norman +Nikia +Naoma +Marna +Margeret +Madaline +Lawana +Kindra +Jutta +Jazmine +Janett +Hannelore +Glendora +Gertrud +Garnett +Freeda +Frederica +Florance +Flavia +Dennis +Carline +Beverlee +Anjanette +Valda +Trinity +Tamala +Stevie +Shonna +Sha +Sarina +Oneida +Micah +Merilyn +Marleen +Lurline +Lenna +Katherin +Jin +Jeni +Hae +Gracia +Glady +Farah +Eric +Enola +Ema +Dominque +Devona +Delana +Cecila +Caprice +Alysha +Ali +Alethia +Vena +Theresia +Tawny +Song +Shakira +Samara +Sachiko +Rachele +Pamella +Nicky +Marni +Mariel +Maren +Malisa +Ligia +Lera +Latoria +Larae +Kimber +Kathern +Karey +Jennefer +Janeth +Halina +Fredia +Delisa +Debroah +Ciera +Chin +Angelika +Andree +Altha +Yen +Vivan +Terresa +Tanna +Suk +Sudie +Soo +Signe +Salena +Ronni +Rebbecca +Myrtie +Mckenzie +Malika +Maida +Loan +Leonarda +Kayleigh +France +Ethyl +Ellyn +Dayle +Cammie +Brittni +Birgit +Avelina +Asuncion +Arianna +Akiko +Venice +Tyesha +Tonie +Tiesha +Takisha +Steffanie +Sindy +Santana +Meghann +Manda +Macie +Lady +Kellye +Kellee +Joslyn +Jason +Inger +Indira +Glinda +Glennis +Fernanda +Faustina +Eneida +Elicia +Dot +Digna +Dell +Arletta +Andre +Willia +Tammara +Tabetha +Sherrell +Sari +Refugio +Rebbeca +Pauletta +Nieves +Natosha +Nakita +Mammie +Kenisha +Kazuko +Kassie +Gary +Earlean +Daphine +Corliss +Clotilde +Carolyne +Bernetta +Augustina +Audrea +Annis +Annabell +Yan +Tennille +Tamica +Selene +Sean +Rosana +Regenia +Qiana +Markita +Macy +Leeanne +Laurine +Kym +Jessenia +Janita +Georgine +Genie +Emiko +Elvie +Deandra +Dagmar +Corie +Collen +Cherish +Romaine +Porsha +Pearlene +Micheline +Merna +Margorie +Margaretta +Lore +Kenneth +Jenine +Hermina +Fredericka +Elke +Drusilla +Dorathy +Dione +Desire +Celena +Brigida +Angeles +Allegra +Theo +Tamekia +Synthia +Stephen +Sook +Slyvia +Rosann +Reatha +Raye +Marquetta +Margart +Ling +Layla +Kymberly +Kiana +Kayleen +Katlyn +Karmen +Joella +Irina +Emelda +Eleni +Detra +Clemmie +Cheryll +Chantell +Cathey +Arnita +Arla +Angle +Angelic +Alyse +Zofia +Thomasine +Tennie +Son +Sherly +Sherley +Sharyl +Remedios +Petrina +Nickole +Myung +Myrle +Mozella +Louanne +Lisha +Latia +Lane +Krysta +Julienne +Joel +Jeanene +Jacqualine +Isaura +Gwenda +Earleen +Donald +Cleopatra +Carlie +Audie +Antonietta +Alise +Alex +Verdell +Val +Tyler +Tomoko +Thao +Talisha +Steven +So +Shemika +Shaun +Scarlet +Savanna +Santina +Rosia +Raeann +Odilia +Nana +Minna +Magan +Lynelle +Le +Karma +Joeann +Ivana +Inell +Ilana +Hye +Honey +Hee +Gudrun +Frank +Dreama +Crissy +Chante +Carmelina +Arvilla +Arthur +Annamae +Alvera +Aleida +Aaron +Yee +Yanira +Vanda +Tianna +Tam +Stefania +Shira +Perry +Nicol +Nancie +Monserrate +Minh +Melynda +Melany +Matthew +Lovella +Laure +Kirby +Kacy +Jacquelynn +Hyon +Gertha +Francisco +Eliana +Christena +Christeen +Charise +Caterina +Carley +Candyce +Arlena +Ammie +Yang +Willette +Vanita +Tuyet +Tiny +Syreeta +Silva +Scott +Ronald +Penney +Nyla +Michal +Maurice +Maryam +Marya +Magen +Ludie +Loma +Livia +Lanell +Kimberlie +Julee +Donetta +Diedra +Denisha +Deane +Dawne +Clarine +Cherryl +Bronwyn +Brandon +Alla +Valery +Tonda +Sueann +Soraya +Shoshana +Shela +Sharleen +Shanelle +Nerissa +Micheal +Meridith +Mellie +Maye +Maple +Magaret +Luis +Lili +Leonila +Leonie +Leeanna +Lavonia +Lavera +Kristel +Kathey +Kathe +Justin +Julian +Jimmy +Jann +Ilda +Hildred +Hildegarde +Genia +Fumiko +Evelin +Ermelinda +Elly +Dung +Doloris +Dionna +Danae +Berneice +Annice +Alix +Verena +Verdie +Tristan +Shawnna +Shawana +Shaunna +Rozella +Randee +Ranae +Milagro +Lynell +Luise +Louie +Loida +Lisbeth +Karleen +Junita +Jona +Isis +Hyacinth +Hedy +Gwenn +Ethelene +Erline +Edward +Donya +Domonique +Delicia +Dannette +Cicely +Branda +Blythe +Bethann +Ashlyn +Annalee +Alline +Yuko +Vella +Trang +Towanda +Tesha +Sherlyn +Narcisa +Miguelina +Meri +Maybell +Marlana +Marguerita +Madlyn +Luna +Lory +Loriann +Liberty +Leonore +Leighann +Laurice +Latesha +Laronda +Katrice +Kasie +Karl +Kaley +Jadwiga +Glennie +Gearldine +Francina +Epifania +Dyan +Dorie +Diedre +Denese +Demetrice +Delena +Darby +Cristie +Cleora +Catarina +Carisa +Bernie +Barbera +Almeta +Trula +Tereasa +Solange +Sheilah +Shavonne +Sanora +Rochell +Mathilde +Margareta +Maia +Lynsey +Lawanna +Launa +Kena +Keena +Katia +Jamey +Glynda +Gaylene +Elvina +Elanor +Danuta +Danika +Cristen +Cordie +Coletta +Clarita +Carmon +Brynn +Azucena +Aundrea +Angele +Yi +Walter +Verlie +Verlene +Tamesha +Silvana +Sebrina +Samira +Reda +Raylene +Penni +Pandora +Norah +Noma +Mireille +Melissia +Maryalice +Laraine +Kimbery +Karyl +Karine +Kam +Jolanda +Johana +Jesusa +Jaleesa +Jae +Jacquelyne +Irish +Iluminada +Hilaria +Hanh +Gennie +Francie +Floretta +Exie +Edda +Drema +Delpha +Bev +Barbar +Assunta +Ardell +Annalisa +Alisia +Yukiko +Yolando +Wonda +Wei +Waltraud +Veta +Tequila +Temeka +Tameika +Shirleen +Shenita +Piedad +Ozella +Mirtha +Marilu +Kimiko +Juliane +Jenice +Jen +Janay +Jacquiline +Hilde +Fe +Fae +Evan +Eugene +Elois +Echo +Devorah +Chau +Brinda +Betsey +Arminda +Aracelis +Apryl +Annett +Alishia +Veola +Usha +Toshiko +Theola +Tashia +Talitha +Shery +Rudy +Renetta +Reiko +Rasheeda +Omega +Obdulia +Mika +Melaine +Meggan +Martin +Marlen +Marget +Marceline +Mana +Magdalen +Librada +Lezlie +Lexie +Latashia +Lasandra +Kelle +Isidra +Isa +Inocencia +Gwyn +Francoise +Erminia +Erinn +Dimple +Devora +Criselda +Armanda +Arie +Ariane +Angelo +Angelena +Allen +Aliza +Adriene +Adaline +Xochitl +Twanna +Tran +Tomiko +Tamisha +Taisha +Susy +Siu +Rutha +Roxy +Rhona +Raymond +Otha +Noriko +Natashia +Merrie +Melvin +Marinda +Mariko +Margert +Loris +Lizzette +Leisha +Kaila +Ka +Joannie +Jerrica +Jene +Jannet +Janee +Jacinda +Herta +Elenore +Doretta +Delaine +Daniell +Claudie +China +Britta +Apolonia +Amberly +Alease +Yuri +Yuk +Wen +Waneta +Ute +Tomi +Sharri +Sandie +Roselle +Reynalda +Raguel +Phylicia +Patria +Olimpia +Odelia +Mitzie +Mitchell +Miss +Minda +Mignon +Mica +Mendy +Marivel +Maile +Lynetta +Lavette +Lauryn +Latrisha +Lakiesha +Kiersten +Kary +Josphine +Jolyn +Jetta +Janise +Jacquie +Ivelisse +Glynis +Gianna +Gaynelle +Emerald +Demetrius +Danyell +Danille +Dacia +Coralee +Cher +Ceola +Brett +Bell +Arianne +Aleshia +Yung +Williemae +Troy +Trinh +Thora +Tai +Svetlana +Sherika +Shemeka +Shaunda +Roseline +Ricki +Melda +Mallie +Lavonna +Latina +Larry +Laquanda +Lala +Lachelle +Klara +Kandis +Johna +Jeanmarie +Jaye +Hang +Grayce +Gertude +Emerita +Ebonie +Clorinda +Ching +Chery +Carola +Breann +Blossom +Bernardine +Becki +Arletha +Argelia +Ara +Alita +Yulanda +Yon +Yessenia +Tobi +Tasia +Sylvie +Shirl +Shirely +Sheridan +Shella +Shantelle +Sacha +Royce +Rebecka +Reagan +Providencia +Paulene +Misha +Miki +Marline +Marica +Lorita +Latoyia +Lasonya +Kerstin +Kenda +Keitha +Kathrin +Jaymie +Jack +Gricelda +Ginette +Eryn +Elina +Elfrieda +Danyel +Cheree +Chanelle +Barrie +Avery +Aurore +Annamaria +Alleen +Ailene +Aide +Yasmine +Vashti +Valentine +Treasa +Tory +Tiffaney +Sheryll +Sharie +Shanae +Sau +Raisa +Pa +Neda +Mitsuko +Mirella +Milda +Maryanna +Maragret +Mabelle +Luetta +Lorina +Letisha +Latarsha +Lanelle +Lajuana +Krissy +Karly +Karena +Jon +Jessika +Jerica +Jeanelle +January +Jalisa +Jacelyn +Izola +Ivey +Gregory +Euna +Etha +Drew +Domitila +Dominica +Daina +Creola +Carli +Camie +Bunny +Brittny +Ashanti +Anisha +Aleen +Adah +Yasuko +Winter +Viki +Valrie +Tona +Tinisha +Thi +Terisa +Tatum +Taneka +Simonne +Shalanda +Serita +Ressie +Refugia +Paz +Olene +Na +Merrill +Margherita +Mandie +Man +Maire +Lyndia +Luci +Lorriane +Loreta +Leonia +Lavona +Lashawnda +Lakia +Kyoko +Krystina +Krysten +Kenia +Kelsi +Jude +Jeanice +Isobel +Georgiann +Genny +Felicidad +Eilene +Deon +Deloise +Deedee +Dannie +Conception +Clora +Cherilyn +Chang +Calandra +Berry +Armandina +Anisa +Ula +Timothy +Tiera +Theressa +Stephania +Sima +Shyla +Shonta +Shera +Shaquita +Shala +Sammy +Rossana +Nohemi +Nery +Moriah +Melita +Melida +Melani +Marylynn +Marisha +Mariette +Malorie +Madelene +Ludivina +Loria +Lorette +Loralee +Lianne +Leon +Lavenia +Laurinda +Lashon +Kit +Kimi +Keila +Katelynn +Kai +Jone +Joane +Ji +Jayna +Janella +Ja +Hue +Hertha +Francene +Elinore +Despina +Delsie +Deedra +Clemencia +Carry +Carolin +Carlos +Bulah +Brittanie +Bok +Blondell +Bibi +Beaulah +Beata +Annita +Agripina +Virgen +Valene +Un +Twanda +Tommye +Toi +Tarra +Tari +Tammera +Shakia +Sadye +Ruthanne +Rochel +Rivka +Pura +Nenita +Natisha +Ming +Merrilee +Melodee +Marvis +Lucilla +Leena +Laveta +Larita +Lanie +Keren +Ileen +Georgeann +Genna +Genesis +Frida +Ewa +Eufemia +Emely +Ela +Edyth +Deonna +Deadra +Darlena +Chanell +Chan +Cathern +Cassondra +Cassaundra +Bernarda +Berna +Arlinda +Anamaria +Albert +Wesley +Vertie +Valeri +Torri +Tatyana +Stasia +Sherise +Sherill +Season +Scottie +Sanda +Ruthe +Rosy +Roberto +Robbi +Ranee +Quyen +Pearly +Palmira +Onita +Nisha +Niesha +Nida +Nevada +Nam +Merlyn +Mayola +Marylouise +Maryland +Marx +Marth +Margene +Madelaine +Londa +Leontine +Leoma +Leia +Lawrence +Lauralee +Lanora +Lakita +Kiyoko +Keturah +Katelin +Kareen +Jonie +Johnette +Jenee +Jeanett +Izetta +Hiedi +Heike +Hassie +Harold +Giuseppina +Georgann +Fidela +Fernande +Elwanda +Ellamae +Eliz +Dusti +Dotty +Cyndy +Coralie +Celesta +Argentina +Alverta +Xenia +Wava +Vanetta +Torrie +Tashina +Tandy +Tambra +Tama +Stepanie +Shila +Shaunta +Sharan +Shaniqua +Shae +Setsuko +Serafina +Sandee +Rosamaria +Priscila +Olinda +Nadene +Muoi +Michelina +Mercedez +Maryrose +Marin +Marcene +Mao +Magali +Mafalda +Logan +Linn +Lannie +Kayce +Karoline +Kamilah +Kamala +Justa +Joline +Jennine +Jacquetta +Iraida +Gerald +Georgeanna +Franchesca +Fairy +Emeline +Elane +Ehtel +Earlie +Dulcie +Dalene +Cris +Classie +Chere +Charis +Caroyln +Carmina +Carita +Brian +Bethanie +Ayako +Arica +An +Alysa +Alessandra +Akilah +Adrien +Zetta +Youlanda +Yelena +Yahaira +Xuan +Wendolyn +Victor +Tijuana +Terrell +Terina +Teresia +Suzi +Sunday +Sherell +Shavonda +Shaunte +Sharda +Shakita +Sena +Ryann +Rubi +Riva +Reginia +Rea +Rachal +Parthenia +Pamula +Monnie +Monet +Michaele +Melia +Marine +Malka +Maisha +Lisandra +Leo +Lekisha +Lean +Laurence +Lakendra +Krystin +Kortney +Kizzie +Kittie +Kera +Kendal +Kemberly +Kanisha +Julene +Jule +Joshua +Johanne +Jeffrey +Jamee +Han +Halley +Gidget +Galina +Fredricka +Fleta +Fatimah +Eusebia +Elza +Eleonore +Dorthey +Doria +Donella +Dinorah +Delorse +Claretha +Christinia +Charlyn +Bong +Belkis +Azzie +Andera +Aiko +Adena +Yer +Yajaira +Wan +Vania +Ulrike +Toshia +Tifany +Stefany +Shizue +Shenika +Shawanna +Sharolyn +Sharilyn +Shaquana +Shantay +See +Rozanne +Roselee +Rickie +Remona +Reanna +Raelene +Quinn +Phung +Petronila +Natacha +Nancey +Myrl +Miyoko +Miesha +Merideth +Marvella +Marquitta +Marhta +Marchelle +Lizeth +Libbie +Lahoma +Ladawn +Kina +Katheleen +Katharyn +Karisa +Kaleigh +Junie +Julieann +Johnsie +Janean +Jaimee +Jackqueline +Hisako +Herma +Helaine +Gwyneth +Glenn +Gita +Eustolia +Emelina +Elin +Edris +Donnette +Donnetta +Dierdre +Denae +Darcel +Claude +Clarisa +Cinderella +Chia +Charlesetta +Charita +Celsa +Cassy +Cassi +Carlee +Bruna +Brittaney +Brande +Billi +Bao +Antonetta +Angla +Angelyn +Analisa +Alane +Wenona +Wendie +Veronique +Vannesa +Tobie +Tempie +Sumiko +Sulema +Sparkle +Somer +Sheba +Shayne +Sharice +Shanel +Shalon +Sage +Roy +Rosio +Roselia +Renay +Rema +Reena +Porsche +Ping +Peg +Ozie +Oretha +Oralee +Oda +Nu +Ngan +Nakesha +Milly +Marybelle +Marlin +Maris +Margrett +Maragaret +Manie +Lurlene +Lillia +Lieselotte +Lavelle +Lashaunda +Lakeesha +Keith +Kaycee +Kalyn +Joya +Joette +Jenae +Janiece +Illa +Grisel +Glayds +Genevie +Gala +Fredda +Fred +Elmer +Eleonor +Debera +Deandrea +Dan +Corrinne +Cordia +Contessa +Colene +Cleotilde +Charlott +Chantay +Cecille +Beatris +Azalee +Arlean +Ardath +Anjelica +Anja +Alfredia +Aleisha +Adam +Zada +Yuonne +Xiao +Willodean +Whitley +Vennie +Vanna +Tyisha +Tova +Torie +Tonisha +Tilda +Tien +Temple +Sirena +Sherril +Shanti +Shan +Senaida +Samella +Robbyn +Renda +Reita +Phebe +Paulita +Nobuko +Nguyet +Neomi +Moon +Mikaela +Melania +Maximina +Marg +Maisie +Lynna +Lilli +Layne +Lashaun +Lakenya +Lael +Kirstie +Kathline +Kasha +Karlyn +Karima +Jovan +Josefine +Jennell +Jacqui +Jackelyn +Hyo +Hien +Grazyna +Florrie +Floria +Eleonora +Dwana +Dorla +Dong +Delmy +Deja +Dede +Dann +Crysta +Clelia +Claris +Clarence +Chieko +Cherlyn +Cherelle +Charmain +Chara +Cammy +Bee +Arnette +Ardelle +Annika +Amiee +Amee +Allena +Yvone +Yuki +Yoshie +Yevette +Yael +Willetta +Voncile +Venetta +Tula +Tonette +Timika +Temika +Telma +Teisha +Taren +Ta +Stacee +Shin +Shawnta +Saturnina +Ricarda +Pok +Pasty +Onie +Nubia +Mora +Mike +Marielle +Mariella +Marianela +Mardell +Many +Luanna +Loise +Lisabeth +Lindsy +Lilliana +Lilliam +Lelah +Leigha +Leanora +Lang +Kristeen +Khalilah +Keeley +Kandra +Junko +Joaquina +Jerlene +Jani +Jamika +Jame +Hsiu +Hermila +Golden +Genevive +Evia +Eugena +Emmaline +Elfreda +Elene +Donette +Delcie +Deeanna +Darcey +Cuc +Clarinda +Cira +Chae +Celinda +Catheryn +Catherin +Casimira +Carmelia +Camellia +Breana +Bobette +Bernardina +Bebe +Basilia +Arlyne +Amal +Alayna +Zonia +Zenia +Yuriko +Yaeko +Wynell +Willow +Willena +Vernia +Tu +Travis +Tora +Terrilyn +Terica +Tenesha +Tawna +Tajuana +Taina +Stephnie +Sona +Sol +Sina +Shondra +Shizuko +Sherlene +Sherice +Sharika +Rossie +Rosena +Rory +Rima +Ria +Rheba +Renna +Peter +Natalya +Nancee +Melodi +Meda +Maxima +Matha +Marketta +Maricruz +Marcelene +Malvina +Luba +Louetta +Leida +Lecia +Lauran +Lashawna +Laine +Khadijah +Katerine +Kasi +Kallie +Julietta +Jesusita +Jestine +Jessia +Jeremy +Jeffie +Janyce +Isadora +Georgianne +Fidelia +Evita +Eura +Eulah +Estefana +Elsy +Elizabet +Eladia +Dodie +Dion +Dia +Denisse +Deloras +Delila +Daysi +Dakota +Curtis +Crystle +Concha +Colby +Claretta +Chu +Christia +Charlsie +Charlena +Carylon +Bettyann +Asley +Ashlea +Amira +Ai +Agueda +Agnus +Yuette +Vinita +Victorina +Tynisha +Treena +Toccara +Tish +Thomasena +Tegan +Soila +Shiloh +Shenna +Sharmaine +Shantae +Shandi +September +Saran +Sarai +Sana +Samuel +Salley +Rosette +Rolande +Regine +Otelia +Oscar +Olevia +Nicholle +Necole +Naida +Myrta +Myesha +Mitsue +Minta +Mertie +Margy +Mahalia +Madalene +Love +Loura +Lorean +Lewis +Lesha +Leonida +Lenita +Lavone +Lashell +Lashandra +Lamonica +Kimbra +Katherina +Karry +Kanesha +Julio +Jong +Jeneva +Jaquelyn +Hwa +Gilma +Ghislaine +Gertrudis +Fransisca +Fermina +Ettie +Etsuko +Ellis +Ellan +Elidia +Edra +Dorethea +Doreatha +Denyse +Denny +Deetta +Daine +Cyrstal +Corrin +Cayla +Carlita +Camila +Burma +Bula +Buena +Blake +Barabara +Avril +Austin +Alaine +Zana +Wilhemina +Wanetta +Virgil +Vi +Veronika +Vernon +Verline +Vasiliki +Tonita +Tisa +Teofila +Tayna +Taunya +Tandra +Takako +Sunni +Suanne +Sixta +Sharell +Seema +Russell +Rosenda +Robena +Raymonde +Pei +Pamila +Ozell +Neida +Neely +Mistie +Micha +Merissa +Maurita +Maryln +Maryetta +Marshall +Marcell +Malena +Makeda +Maddie +Lovetta +Lourie +Lorrine +Lorilee +Lester +Laurena +Lashay +Larraine +Laree +Lacresha +Kristle +Krishna +Keva +Keira +Karole +Joie +Jinny +Jeannetta +Jama +Heidy +Gilberte +Gema +Faviola +Evelynn +Enda +Elli +Ellena +Divina +Dagny +Collene +Codi +Cindie +Chassidy +Chasidy +Catrice +Catherina +Cassey +Caroll +Carlena +Candra +Calista +Bryanna +Britteny +Beula +Bari +Audrie +Audria +Ardelia +Annelle +Angila +Alona +Allyn +Douglas +Roger +Jonathan +Ralph +Nicholas +Benjamin +Bruce +Harry +Wayne +Steve +Howard +Ernest +Phillip +Todd +Craig +Alan +Philip +Earl +Danny +Bryan +Stanley +Leonard +Nathan +Manuel +Rodney +Marvin +Vincent +Jeffery +Jeff +Chad +Jacob +Alfred +Bradley +Herbert +Frederick +Edwin +Don +Ricky +Randall +Barry +Bernard +Leroy +Marcus +Theodore +Clifford +Miguel +Jim +Tom +Calvin +Bill +Lloyd +Derek +Warren +Darrell +Jerome +Floyd +Alvin +Tim +Gordon +Greg +Jorge +Dustin +Pedro +Derrick +Zachary +Herman +Glen +Hector +Ricardo +Rick +Brent +Ramon +Gilbert +Marc +Reginald +Ruben +Nathaniel +Rafael +Edgar +Milton +Raul +Ben +Chester +Duane +Franklin +Brad +Ron +Roland +Arnold +Harvey +Jared +Erik +Darryl +Neil +Javier +Fernando +Clinton +Ted +Mathew +Tyrone +Darren +Lance +Kurt +Allan +Nelson +Guy +Clayton +Hugh +Max +Dwayne +Dwight +Armando +Felix +Everett +Ian +Wallace +Ken +Bob +Alfredo +Alberto +Dave +Ivan +Byron +Isaac +Morris +Clifton +Willard +Ross +Andy +Salvador +Kirk +Sergio +Seth +Kent +Terrance +Eduardo +Terrence +Enrique +Wade +Stuart +Fredrick +Arturo +Alejandro +Nick +Luther +Wendell +Jeremiah +Julius +Otis +Trevor +Oliver +Luke +Homer +Gerard +Doug +Kenny +Hubert +Lyle +Matt +Alfonso +Orlando +Rex +Carlton +Ernesto +Neal +Pablo +Lorenzo +Omar +Wilbur +Grant +Horace +Roderick +Abraham +Willis +Rickey +Andres +Cesar +Johnathan +Malcolm +Rudolph +Damon +Kelvin +Preston +Alton +Archie +Marco +Wm +Pete +Randolph +Garry +Geoffrey +Jonathon +Felipe +Gerardo +Ed +Dominic +Delbert +Colin +Guillermo +Earnest +Lucas +Benny +Spencer +Rodolfo +Myron +Edmund +Garrett +Salvatore +Cedric +Lowell +Gregg +Sherman +Wilson +Sylvester +Roosevelt +Israel +Jermaine +Forrest +Wilbert +Leland +Simon +Clark +Irving +Bryant +Owen +Rufus +Woodrow +Kristopher +Mack +Levi +Marcos +Gustavo +Jake +Lionel +Gilberto +Clint +Nicolas +Ismael +Orville +Ervin +Dewey +Al +Wilfred +Josh +Hugo +Ignacio +Caleb +Tomas +Sheldon +Erick +Stewart +Doyle +Darrel +Rogelio +Terence +Santiago +Alonzo +Elias +Bert +Elbert +Ramiro +Conrad +Noah +Grady +Phil +Cornelius +Lamar +Rolando +Clay +Percy +Dexter +Bradford +Darin +Amos +Moses +Irvin +Saul +Roman +Randal +Timmy +Darrin +Winston +Brendan +Abel +Dominick +Boyd +Emilio +Elijah +Domingo +Emmett +Marlon +Emanuel +Jerald +Edmond +Emil +Dewayne +Will +Otto +Teddy +Reynaldo +Bret +Jess +Trent +Humberto +Emmanuel +Stephan +Vicente +Lamont +Garland +Miles +Efrain +Heath +Rodger +Harley +Ethan +Eldon +Rocky +Pierre +Junior +Freddy +Eli +Bryce +Antoine +Sterling +Chase +Grover +Elton +Cleveland +Dylan +Chuck +Damian +Reuben +Stan +August +Leonardo +Jasper +Russel +Erwin +Benito +Hans +Monte +Blaine +Ernie +Curt +Quentin +Agustin +Murray +Jamal +Adolfo +Harrison +Tyson +Burton +Brady +Elliott +Wilfredo +Bart +Jarrod +Vance +Denis +Damien +Joaquin +Harlan +Desmond +Elliot +Darwin +Gregorio +Buddy +Xavier +Kermit +Roscoe +Esteban +Anton +Solomon +Scotty +Norbert +Elvin +Williams +Nolan +Rod +Quinton +Hal +Brain +Rob +Elwood +Kendrick +Darius +Moises +Fidel +Thaddeus +Cliff +Marcel +Jackson +Raphael +Bryon +Armand +Alvaro +Jeffry +Dane +Joesph +Thurman +Ned +Rusty +Monty +Fabian +Reggie +Mason +Graham +Isaiah +Vaughn +Gus +Loyd +Diego +Adolph +Norris +Millard +Rocco +Gonzalo +Derick +Rodrigo +Wiley +Rigoberto +Alphonso +Ty +Noe +Vern +Reed +Jefferson +Elvis +Bernardo +Mauricio +Hiram +Donovan +Basil +Riley +Nickolas +Maynard +Scot +Vince +Quincy +Eddy +Sebastian +Federico +Ulysses +Heriberto +Donnell +Cole +Davis +Gavin +Emery +Ward +Romeo +Jayson +Dante +Clement +Coy +Maxwell +Jarvis +Bruno +Issac +Dudley +Brock +Sanford +Carmelo +Barney +Nestor +Stefan +Donny +Art +Linwood +Beau +Weldon +Galen +Isidro +Truman +Delmar +Johnathon +Silas +Frederic +Dick +Irwin +Merlin +Charley +Marcelino +Harris +Carlo +Trenton +Kurtis +Hunter +Aurelio +Winfred +Vito +Collin +Denver +Carter +Leonel +Emory +Pasquale +Mohammad +Mariano +Danial +Landon +Dirk +Branden +Adan +Buford +German +Wilmer +Emerson +Zachery +Fletcher +Jacques +Errol +Dalton +Monroe +Josue +Edwardo +Booker +Wilford +Sonny +Shelton +Carson +Theron +Raymundo +Daren +Houston +Robby +Lincoln +Genaro +Bennett +Octavio +Cornell +Hung +Arron +Antony +Herschel +Giovanni +Garth +Cyrus +Cyril +Ronny +Lon +Freeman +Duncan +Kennith +Carmine +Erich +Chadwick +Wilburn +Russ +Reid +Myles +Anderson +Morton +Jonas +Forest +Mitchel +Mervin +Zane +Rich +Jamel +Lazaro +Alphonse +Randell +Major +Jarrett +Brooks +Abdul +Luciano +Seymour +Eugenio +Mohammed +Valentin +Chance +Arnulfo +Lucien +Ferdinand +Thad +Ezra +Aldo +Rubin +Royal +Mitch +Earle +Abe +Wyatt +Marquis +Lanny +Kareem +Jamar +Boris +Isiah +Emile +Elmo +Aron +Leopoldo +Everette +Josef +Eloy +Rodrick +Reinaldo +Lucio +Jerrod +Weston +Hershel +Barton +Parker +Lemuel +Burt +Jules +Gil +Eliseo +Ahmad +Nigel +Efren +Antwan +Alden +Margarito +Coleman +Dino +Osvaldo +Les +Deandre +Normand +Kieth +Trey +Norberto +Napoleon +Jerold +Fritz +Rosendo +Milford +Christoper +Alfonzo +Lyman +Josiah +Brant +Wilton +Rico +Jamaal +Dewitt +Brenton +Olin +Foster +Faustino +Claudio +Judson +Gino +Edgardo +Alec +Tanner +Jarred +Donn +Tad +Prince +Porfirio +Odis +Lenard +Chauncey +Tod +Mel +Marcelo +Kory +Augustus +Keven +Hilario +Bud +Sal +Orval +Mauro +Zachariah +Olen +Anibal +Milo +Jed +Dillon +Amado +Newton +Lenny +Richie +Horacio +Brice +Mohamed +Delmer +Dario +Reyes +Mac +Jonah +Jerrold +Robt +Hank +Rupert +Rolland +Kenton +Damion +Antone +Waldo +Fredric +Bradly +Kip +Burl +Walker +Tyree +Jefferey +Ahmed +Willy +Stanford +Oren +Noble +Moshe +Mikel +Enoch +Brendon +Quintin +Jamison +Florencio +Darrick +Tobias +Hassan +Giuseppe +Demarcus +Cletus +Tyrell +Lyndon +Keenan +Werner +Geraldo +Columbus +Chet +Bertram +Markus +Huey +Hilton +Dwain +Donte +Tyron +Omer +Isaias +Hipolito +Fermin +Adalberto +Bo +Barrett +Teodoro +Mckinley +Maximo +Garfield +Raleigh +Lawerence +Abram +Rashad +King +Emmitt +Daron +Samual +Miquel +Eusebio +Domenic +Darron +Buster +Wilber +Renato +Jc +Hoyt +Haywood +Ezekiel +Chas +Florentino +Elroy +Clemente +Arden +Neville +Edison +Deshawn +Nathanial +Jordon +Danilo +Claud +Sherwood +Raymon +Rayford +Cristobal +Ambrose +Titus +Hyman +Felton +Ezequiel +Erasmo +Stanton +Lonny +Len +Ike +Milan +Lino +Jarod +Herb +Andreas +Walton +Rhett +Palmer +Douglass +Cordell +Oswaldo +Ellsworth +Virgilio +Toney +Nathanael +Del +Benedict +Mose +Johnson +Isreal +Garret +Fausto +Asa +Arlen +Zack +Warner +Modesto +Francesco +Manual +Gaylord +Gaston +Filiberto +Deangelo +Michale +Granville +Wes +Malik +Zackary +Tuan +Eldridge +Cristopher +Cortez +Antione +Malcom +Long +Korey +Jospeh +Colton +Waylon +Von +Hosea +Shad +Santo +Rudolf +Rolf +Rey +Renaldo +Marcellus +Lucius +Kristofer +Boyce +Benton +Hayden +Harland +Arnoldo +Rueben +Leandro +Kraig +Jerrell +Jeromy +Hobert +Cedrick +Arlie +Winford +Wally +Luigi +Keneth +Jacinto +Graig +Franklyn +Edmundo +Sid +Porter +Leif +Jeramy +Buck +Willian +Vincenzo +Shon +Lynwood +Jere +Hai +Elden +Dorsey +Darell +Broderick +Alonso diff --git a/misc/benchmarks/misc/prng_bench.cpp b/misc/benchmarks/misc/prng_bench.cpp new file mode 100644 index 00000000..6f4e0e47 --- /dev/null +++ b/misc/benchmarks/misc/prng_bench.cpp @@ -0,0 +1,223 @@ +#include <cstdint> +#include <iostream> +#include <ctime> +#include <random> +#include <stc/crandom.h> + +static inline uint64_t rotl64(const uint64_t x, const int k) + { return (x << k) | (x >> (64 - k)); } + +static uint64_t splitmix64_x = 87213627321ull; /* The state can be seeded with any value. */ + +uint64_t splitmix64(void) { + uint64_t z = (splitmix64_x += 0x9e3779b97f4a7c15); + z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9; + z = (z ^ (z >> 27)) * 0x94d049bb133111eb; + return z ^ (z >> 31); +} + +static void init_state(uint64_t *rng, uint64_t seed) { + splitmix64_x = seed; + for (int i=0; i<4; ++i) rng[i] = splitmix64(); +} + +/* romu_trio */ + +uint64_t romu_trio(uint64_t s[3]) { + uint64_t xp = s[0], yp = s[1], zp = s[2]; + s[0] = 15241094284759029579u * zp; + s[1] = yp - xp; s[1] = rotl64(s[1], 12); + s[2] = zp - yp; s[2] = rotl64(s[2], 44); + return xp; +} + +/* sfc64 */ + +static inline uint64_t sfc64(uint64_t s[4]) { + uint64_t result = s[0] + s[1] + s[3]++; + s[0] = s[1] ^ (s[1] >> 11); + s[1] = s[2] + (s[2] << 3); + s[2] = rotl64(s[2], 24) + result; + return result; +} + +uint32_t sfc32(uint32_t s[4]) { + uint32_t t = s[0] + s[1] + s[3]++; + s[0] = s[1] ^ (s[1] >> 9); + s[1] = s[2] + (s[2] << 3); + s[2] = ((s[2] << 21) | (s[2] >> 11)) + t; + return t; +} + +uint32_t stc32(uint32_t s[5]) { + uint32_t t = (s[0] ^ (s[3] += s[4])) + s[1]; + s[0] = s[1] ^ (s[1] >> 9); + s[1] = s[2] + (s[2] << 3); + s[2] = ((s[2] << 21) | (s[2] >> 11)) + t; + return t; +} + +uint32_t pcg32(uint32_t s[2]) { + uint64_t oldstate = s[0]; + s[0] = oldstate * 6364136223846793005ULL + (s[1]|1); + uint32_t xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u; + uint32_t rot = oldstate >> 59u; + return (xorshifted >> rot) | (xorshifted << ((-rot) & 31)); +} + + +/* xoshiro128+ */ + +uint64_t xoroshiro128plus(uint64_t s[2]) { + const uint64_t s0 = s[0]; + uint64_t s1 = s[1]; + const uint64_t result = s0 + s1; + + s1 ^= s0; + s[0] = rotl64(s0, 24) ^ s1 ^ (s1 << 16); // a, b + s[1] = rotl64(s1, 37); // c + + return result; +} + + +/* xoshiro256** */ + +static inline uint64_t xoshiro256starstar(uint64_t s[4]) { + const uint64_t result = rotl64(s[1] * 5, 7) * 9; + const uint64_t t = s[1] << 17; + s[2] ^= s[0]; + s[3] ^= s[1]; + s[1] ^= s[2]; + s[0] ^= s[3]; + s[2] ^= t; + s[3] = rotl64(s[3], 45); + return result; +} + +// wyrand - 2020-12-07 +static inline void _wymum(uint64_t *A, uint64_t *B){ +#if defined(__SIZEOF_INT128__) + __uint128_t r = *A; r *= *B; + *A = (uint64_t) r; *B = (uint64_t ) (r >> 64); +#elif defined(_MSC_VER) && defined(_M_X64) + *A = _umul128(*A, *B, B); +#else + uint64_t ha=*A>>32, hb=*B>>32, la=(uint32_t)*A, lb=(uint32_t)*B, hi, lo; + uint64_t rh=ha*hb, rm0=ha*lb, rm1=hb*la, rl=la*lb, t=rl+(rm0<<32), c=t<rl; + lo=t+(rm1<<32); c+=lo<t; hi=rh+(rm0>>32)+(rm1>>32)+c; + *A=lo; *B=hi; +#endif +} +static inline uint64_t _wymix(uint64_t A, uint64_t B){ + _wymum(&A,&B); return A^B; +} +static inline uint64_t wyrand64(uint64_t *seed){ + static const uint64_t _wyp[] = {0xa0761d6478bd642full, 0xe7037ed1a0b428dbull}; + *seed+=_wyp[0]; return _wymix(*seed,*seed^_wyp[1]); +} + + +using namespace std; + +int main(void) +{ + enum {N = 2000000000}; + uint16_t* recipient = new uint16_t[N]; + static stc64_t rng; + init_state(rng.state, 12345123); + std::mt19937 mt(12345123); + + cout << "WARMUP" << endl; + for (size_t i = 0; i < N; i++) + recipient[i] = wyrand64(rng.state); + + clock_t beg, end; + for (size_t ti = 0; ti < 2; ti++) { + init_state(rng.state, 12345123); + cout << endl << "ROUND " << ti+1 << " ---------" << endl; + + beg = clock(); + for (size_t i = 0; i < N; i++) + recipient[i] = romu_trio(rng.state); + end = clock(); + cout << "romu_trio:\t" + << (float(end - beg) / CLOCKS_PER_SEC) + << "s: " << recipient[312] << endl; + + beg = clock(); + for (size_t i = 0; i < N; i++) + recipient[i] = wyrand64(rng.state); + end = clock(); + cout << "wyrand64:\t" + << (float(end - beg) / CLOCKS_PER_SEC) + << "s: " << recipient[312] << endl; + + beg = clock(); + for (size_t i = 0; i < N; i++) + recipient[i] = sfc32((uint32_t *)rng.state); + end = clock(); + cout << "sfc32:\t\t" + << (float(end - beg) / CLOCKS_PER_SEC) + << "s: " << recipient[312] << endl; + + beg = clock(); + for (size_t i = 0; i < N; i++) + recipient[i] = stc32((uint32_t *)rng.state); + end = clock(); + cout << "stc32:\t\t" + << (float(end - beg) / CLOCKS_PER_SEC) + << "s: " << recipient[312] << endl; + + beg = clock(); + for (size_t i = 0; i < N; i++) + recipient[i] = pcg32((uint32_t *)rng.state); + end = clock(); + cout << "pcg32:\t\t" + << (float(end - beg) / CLOCKS_PER_SEC) + << "s: " << recipient[312] << endl; + + beg = clock(); + for (size_t i = 0; i < N; i++) + recipient[i] = sfc64(rng.state); + end = clock(); + cout << "sfc64:\t\t" + << (float(end - beg) / CLOCKS_PER_SEC) + << "s: " << recipient[312] << endl; + + beg = clock(); + for (size_t i = 0; i < N; i++) + recipient[i] = stc64_rand(&rng); + end = clock(); + cout << "stc64:\t\t" + << (float(end - beg) / CLOCKS_PER_SEC) + << "s: " << recipient[312] << endl; + + + beg = clock(); + for (size_t i = 0; i < N; i++) + recipient[i] = xoroshiro128plus(rng.state); + end = clock(); + cout << "xoroshiro128+:\t" + << (float(end - beg) / CLOCKS_PER_SEC) + << "s: " << recipient[312] << endl; + + beg = clock(); + for (size_t i = 0; i < N; i++) + recipient[i] = xoshiro256starstar(rng.state); + end = clock(); + cout << "xoshiro256**:\t" + << (float(end - beg) / CLOCKS_PER_SEC) + << "s: " << recipient[312] << endl; + + beg = clock(); + for (size_t i = 0; i < N; i++) + recipient[i] = mt(); + end = clock(); + cout << "std::mt19937:\t" + << (float(end - beg) / CLOCKS_PER_SEC) + << "s: " << recipient[312] << endl; + } + delete[] recipient; + return 0; +} diff --git a/misc/benchmarks/misc/rust_cmap.c b/misc/benchmarks/misc/rust_cmap.c new file mode 100644 index 00000000..83b7dd19 --- /dev/null +++ b/misc/benchmarks/misc/rust_cmap.c @@ -0,0 +1,61 @@ +#include <time.h> +#include <stdio.h> +#define i_key uint64_t +#define i_val uint64_t +#define i_tag u64 +#define i_max_load_factor 0.8f +#include <stc/cmap.h> + +uint64_t romu_rotl(uint64_t val, uint32_t r) { + return (val << r) | (val >> (64 - r)); +} + +uint64_t romu_trio(uint64_t s[3]) { + const uint64_t xp = s[0], + yp = s[1], + zp = s[2]; + s[0] = 15241094284759029579u * zp; + s[1] = yp - xp; + s[1] = romu_rotl(s[1], 12); + s[2] = zp - yp; + s[2] = romu_rotl(s[2], 44); + return xp; +} + +int main() +{ + c_auto (cmap_u64, m) { + const size_t n = 50000000, + mask = (1 << 25) - 1, + ms = CLOCKS_PER_SEC/1000; + cmap_u64_reserve(&m, n); + printf("STC cmap n = %" c_ZU ", mask = 0x%" PRIxMAX "\n", n, mask); + + uint64_t rng[3] = {1872361123, 123879177, 87739234}, sum; + clock_t now = clock(); + c_forrange (n) { + uint64_t key = romu_trio(rng) & mask; + cmap_u64_insert(&m, key, 0).ref->second += 1; + } + printf("insert : %" c_ZU "ms \tsize : %" c_ZU "\n", (clock() - now)/ms, cmap_u64_size(&m)); + + now = clock(); + sum = 0; + c_forrange (key, mask + 1) { sum += cmap_u64_contains(&m, key); } + printf("lookup : %" c_ZU "ms \tsum : %" c_ZU "\n", (clock() - now)/ms, sum); + + now = clock(); + sum = 0; + c_foreach (i, cmap_u64, m) { sum += i.ref->second; } + printf("iterate : %" c_ZU "ms \tsum : %" c_ZU "\n", (clock() - now)/ms, sum); + + uint64_t rng2[3] = {1872361123, 123879177, 87739234}; + now = clock(); + c_forrange (n) { + uint64_t key = romu_trio(rng2) & mask; + cmap_u64_erase(&m, key); + } + printf("remove : %" c_ZU "ms \tsize : %" c_ZU "\n", (clock() - now)/ms, cmap_u64_size(&m)); + printf("press a key:\n"); getchar(); + } +} diff --git a/misc/benchmarks/misc/rust_hashmap.rs b/misc/benchmarks/misc/rust_hashmap.rs new file mode 100644 index 00000000..5394a7c3 --- /dev/null +++ b/misc/benchmarks/misc/rust_hashmap.rs @@ -0,0 +1,82 @@ +use std::{ + hash::{BuildHasherDefault, Hasher}, + io::Read, + time::Instant, +}; + +struct MyHasher { + seed: u64, +} + +impl Default for MyHasher { + fn default() -> Self { + Self { seed: 0xb5ad4eceda1ce2a9_u64 } + } +} + +impl Hasher for MyHasher { + fn write(&mut self, bytes: &[u8]) { + use std::convert::TryInto; + self.seed = u64::from_ne_bytes(bytes.try_into().unwrap()).wrapping_mul(0xc6a4a7935bd1e99d); + } + + #[inline] + fn write_u64(&mut self, i: u64) { + self.seed = i.wrapping_mul(0xc6a4a7935bd1e99d); + } + + #[inline] + fn finish(&self) -> u64 { + self.seed + } +} + +type MyBuildHasher = BuildHasherDefault<MyHasher>; + +fn romu_trio(s: &mut [u64]) -> u64 { + let xp = s[0]; + let yp = s[1]; + let zp = s[2]; + s[0] = 15241094284759029579_u64.wrapping_mul(zp); + s[1] = yp.wrapping_sub(xp); + s[1] = s[1].rotate_left(12); + s[2] = zp.wrapping_sub(yp); + s[2] = s[2].rotate_left(44); + return xp; +} + +fn main() { + let n = 50_000_000; + let mask = (1 << 25) - 1; + + let mut m = std::collections::HashMap::<u64, u64, MyBuildHasher>::default(); + m.reserve(n); + + let mut rng: [u64; 3] = [1872361123, 123879177, 87739234]; + println!("Rust HashMap n = {}, mask = {:#x}", n, mask); + let now = Instant::now(); + for _i in 0..n { + let key: u64 = romu_trio(&mut rng) & mask; + *m.entry(key).or_insert(0) += 1; + } + println!("insert : {}ms \tsize : {}", now.elapsed().as_millis(), m.len()); + let now = Instant::now(); + let mut sum = 0; + for i in 0..mask + 1 { if m.contains_key(&i) { sum += 1; }} + println!("lookup : {}ms \tsum : {}", now.elapsed().as_millis(), sum); + + let now = Instant::now(); + let mut sum = 0; + for (_, value) in &m { sum += value; } + println!("iterate : {}ms \tsum : {}", now.elapsed().as_millis(), sum); + + let mut rng: [u64; 3] = [1872361123, 123879177, 87739234]; + let now = Instant::now(); + for _ in 0..n { + let key: u64 = romu_trio(&mut rng) & mask; + m.remove(&key); + } + println!("remove : {}ms \tsize : {}", now.elapsed().as_millis(), m.len()); + println!("press a key:"); + std::io::stdin().bytes().next(); +}
\ No newline at end of file diff --git a/misc/benchmarks/misc/sso_bench.cpp b/misc/benchmarks/misc/sso_bench.cpp new file mode 100644 index 00000000..0fffef7a --- /dev/null +++ b/misc/benchmarks/misc/sso_bench.cpp @@ -0,0 +1,135 @@ +#include <string> +#include <iostream> +#include <chrono> + +#include <stc/crandom.h> +#include <stc/cstr.h> + +#define i_type StcVec +#define i_val_str +#include <stc/cstack.h> + +#define i_type StcSet +#define i_val_str +#include <stc/csset.h> + +#include <vector> +using StdVec = std::vector<std::string>; +#include <set> +using StdSet = std::set<std::string>; + + +static const int BENCHMARK_SIZE = 2000000; +static const int MAX_STRING_SIZE = 50; +static const char CHARS[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz=+-"; +using time_point = std::chrono::high_resolution_clock::time_point; + + +static inline std::string randomString_STD(int strsize) { + std::string s(strsize, 0); + char* p = &s[0]; + union { uint64_t u8; uint8_t b[8]; } r; + for (int i = 0; i < strsize; ++i) { + if ((i & 7) == 0) r.u8 = crandom() & 0x3f3f3f3f3f3f3f3f; + p[i] = CHARS[r.b[i & 7]]; + } + return s; +} + +static inline cstr randomString_STC(int strsize) { + cstr s = cstr_with_size(strsize, 0); + char* p = cstr_data(&s); + union { uint64_t u8; uint8_t b[8]; } r; + for (int i = 0; i < strsize; ++i) { + if ((i & 7) == 0) r.u8 = crandom() & 0x3f3f3f3f3f3f3f3f; + p[i] = CHARS[r.b[i & 7]]; + } + return s; +} + + +void addRandomString(StdVec& vec, int strsize) { + vec.push_back(std::move(randomString_STD(strsize))); +} + +void addRandomString(StcVec& vec, int strsize) { + StcVec_push(&vec, randomString_STC(strsize)); +} + +void addRandomString(StdSet& set, int strsize) { + set.insert(std::move(randomString_STD(strsize))); +} + +void addRandomString(StcSet& set, int strsize) { + StcSet_insert(&set, randomString_STC(strsize)); +} + + +template <class C> +int benchmark(C& container, const int n, const int strsize) { + time_point t1 = std::chrono::high_resolution_clock::now(); + + for (int i = 0; i < n; i++) + addRandomString(container, strsize); + + time_point t2 = std::chrono::high_resolution_clock::now(); + const auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count(); + std::cerr << (strsize ? strsize : 32) << "\t" << duration; + return (int)duration; +} + + +int main() { + uint64_t seed = 4321; + int sum, n; + + // VECTOR WITH STRINGS + + csrandom(seed); + sum = 0, n = 0; + std::cerr << "\nstrsize\tmsecs\tstd::vector<std::string>, size=" << BENCHMARK_SIZE << "\n"; + for (int strsize = 1; strsize <= MAX_STRING_SIZE; strsize += 2) { + StdVec vec; vec.reserve(BENCHMARK_SIZE); + sum += benchmark(vec, BENCHMARK_SIZE, strsize), ++n; + std::cout << '\t' << vec.front() << '\n'; + } + std::cout << "Avg:\t" << sum/n << '\n'; + + csrandom(seed); + sum = 0, n = 0; + std::cerr << "\nstrsize\tmsecs\tcvec<cstr>, size=" << BENCHMARK_SIZE << "\n"; + for (int strsize = 1; strsize <= MAX_STRING_SIZE; strsize += 2) { + StcVec vec = StcVec_with_capacity(BENCHMARK_SIZE); + sum += benchmark(vec, BENCHMARK_SIZE, strsize), ++n; + std::cout << '\t' << cstr_str(&vec.data[0]) << '\n'; + StcVec_drop(&vec); + } + std::cout << "Avg:\t" << sum/n << '\n'; + + // SORTED SET WITH STRINGS + + csrandom(seed); + sum = 0, n = 0; + std::cerr << "\nstrsize\tmsecs\tstd::set<std::string>, size=" << BENCHMARK_SIZE/16 << "\n"; + for (int strsize = 1; strsize <= MAX_STRING_SIZE; strsize += 2) { + StdSet set; + sum += benchmark(set, BENCHMARK_SIZE/16, strsize), ++n; + std::cout << '\t' << *set.begin() << '\n'; + } + std::cout << "Avg:\t" << sum/n << '\n'; + + csrandom(seed); + sum = 0, n = 0; + std::cerr << "\nstrsize\tmsecs\tcsset<cstr>, size=" << BENCHMARK_SIZE/16 << "\n"; + for (int strsize = 1; strsize <= MAX_STRING_SIZE; strsize += 2) { + StcSet set = StcSet_with_capacity(BENCHMARK_SIZE/16); + sum += benchmark(set, BENCHMARK_SIZE/16, strsize), ++n; + std::cout << '\t' << cstr_str(StcSet_front(&set)) << '\n'; + StcSet_drop(&set); + } + std::cout << "Avg:\t" << sum/n << '\n'; + + std::cerr << "sizeof(std::string) : " << sizeof(std::string) << std::endl + << "sizeof(cstr) : " << sizeof(cstr) << std::endl; + return 0; +} diff --git a/misc/benchmarks/misc/string_bench_STC.cpp b/misc/benchmarks/misc/string_bench_STC.cpp new file mode 100644 index 00000000..ae8e4c38 --- /dev/null +++ b/misc/benchmarks/misc/string_bench_STC.cpp @@ -0,0 +1,300 @@ +// https://www.codeproject.com/Tips/5255442/Cplusplus14-20-Heterogeneous-Lookup-Benchmark +// https://github.com/shaovoon/cpp_hetero_lookup_bench + +#include <iostream> +#include <iomanip> +#include <chrono> +#define i_static +#include <stc/cstr.h> // string +#define i_static +#include <stc/csview.h> // string_view + +#define i_key_str +#include <stc/cvec.h> // vec of cstr with const char* lookup + +#define i_type cvec_sv // override default type name (cvec_csview) +#define i_key csview +#define i_cmp csview_cmp +#include <stc/cvec.h> // cvec_vs: vec of csview + +#define i_key_str +#define i_val size_t +#include <stc/csmap.h> // sorted map of cstr, const char* lookup + +#define i_key_ssv +#define i_val size_t +#include <stc/csmap.h> // sorted map of cstr, csview lookup + +#define i_key_str +#define i_val size_t +#include <stc/cmap.h> // unordered map of cstr, const char* lookup + +#define i_key_ssv +#define i_val size_t +#include <stc/cmap.h> // unordered map of cstr, csview lookup + + +cvec_str read_file(const char* name) +{ + cvec_str data = cvec_str_init(); + c_auto (cstr, line) + c_with (FILE* f = fopen(name, "r"), fclose(f)) + while (cstr_getline(&line, f)) + cvec_str_emplace_back(&data, cstr_str(&line)); + return data; +} + +class timer +{ +public: + timer() = default; + void start(const std::string& text_) + { + text = text_; + begin = std::chrono::high_resolution_clock::now(); + } + void stop() + { + auto end = std::chrono::high_resolution_clock::now(); + auto dur = end - begin; + auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(dur).count(); + std::cout << std::setw(32) << text << " timing:" << std::setw(5) << ms << "ms" << std::endl; + } + +private: + std::string text; + std::chrono::high_resolution_clock::time_point begin; +}; + +void initShortStringVec(cvec_str* vs, cvec_sv* vsv) +{ + cvec_str_drop(vs); + cvec_sv_clear(vsv); + + *vs = read_file("names.txt"); +/* + cvec_str_emplace_back(vs, "Susan"); + cvec_str_emplace_back(vs, "Jason"); + cvec_str_emplace_back(vs, "Lily"); + cvec_str_emplace_back(vs, "Michael"); + cvec_str_emplace_back(vs, "Mary"); + + cvec_str_emplace_back(vs, "Jerry"); + cvec_str_emplace_back(vs, "Jenny"); + cvec_str_emplace_back(vs, "Klaus"); + cvec_str_emplace_back(vs, "Celine"); + cvec_str_emplace_back(vs, "Kenny"); + + cvec_str_emplace_back(vs, "Kelly"); + cvec_str_emplace_back(vs, "Jackson"); + cvec_str_emplace_back(vs, "Mandy"); + cvec_str_emplace_back(vs, "Terry"); + cvec_str_emplace_back(vs, "Sandy"); + + cvec_str_emplace_back(vs, "Billy"); + cvec_str_emplace_back(vs, "Cindy"); + cvec_str_emplace_back(vs, "Phil"); + cvec_str_emplace_back(vs, "Lindy"); + cvec_str_emplace_back(vs, "David"); +*/ + size_t num = 0; + c_foreach (i, cvec_str, *vs) + { + cvec_sv_push_back(vsv, cstr_sv(i.ref)); + num += cstr_size(i.ref); + } + std::cout << "num strings: " << cvec_sv_size(vsv) << std::endl; + std::cout << "avg str len: " << num / (float)cvec_sv_size(vsv) << std::endl; +} + +void initLongStringVec(cvec_str* vs, cvec_sv* vsv) +{ + cvec_str_drop(vs); + cvec_sv_clear(vsv); + + *vs = read_file("names.txt"); + c_foreach (i, cvec_str, *vs) { + cstr_append_s(i.ref, *i.ref); + cstr_append_s(i.ref, *i.ref); + cstr_append_s(i.ref, *i.ref); + } +/* + cvec_str_emplace_back(vs, "Susan Susan Susan Susan Susan Susan"); + cvec_str_emplace_back(vs, "Jason Jason Jason Jason Jason Jason"); + cvec_str_emplace_back(vs, "Lily Lily Lily Lily Lily Lily"); + cvec_str_emplace_back(vs, "Michael Michael Michael Michael Michael Michael"); + cvec_str_emplace_back(vs, "Mary Mary Mary Mary Mary Mary"); + + cvec_str_emplace_back(vs, "Jerry Jerry Jerry Jerry Jerry Jerry"); + cvec_str_emplace_back(vs, "Jenny Jenny Jenny Jenny Jenny Jenny"); + cvec_str_emplace_back(vs, "Klaus Klaus Klaus Klaus Klaus Klaus"); + cvec_str_emplace_back(vs, "Celine Celine Celine Celine Celine Celine"); + cvec_str_emplace_back(vs, "Kenny Kenny Kenny Kenny Kenny Kenny"); + + cvec_str_emplace_back(vs, "Kelly Kelly Kelly Kelly Kelly Kelly"); + cvec_str_emplace_back(vs, "Jackson Jackson Jackson Jackson Jackson Jackson"); + cvec_str_emplace_back(vs, "Mandy Mandy Mandy Mandy Mandy Mandy"); + cvec_str_emplace_back(vs, "Terry Terry Terry Terry Terry Terry"); + cvec_str_emplace_back(vs, "Sandy Sandy Sandy Sandy Sandy Sandy"); + + cvec_str_emplace_back(vs, "Billy Billy Billy Billy Billy Billy"); + cvec_str_emplace_back(vs, "Cindy Cindy Cindy Cindy Cindy Cindy"); + cvec_str_emplace_back(vs, "Phil Phil Phil Phil Phil Phil"); + cvec_str_emplace_back(vs, "Lindy Lindy Lindy Lindy Lindy Lindy"); + cvec_str_emplace_back(vs, "David David David David David David"); +*/ + size_t num = 0; + c_foreach (i, cvec_str, *vs) + { + cvec_sv_push_back(vsv, cstr_sv(i.ref)); + num += cstr_size(i.ref); + } + std::cout << "num strings: " << cvec_sv_size(vsv) << std::endl; + std::cout << "avg str len: " << num / (float)cvec_sv_size(vsv) << std::endl; +} + +void initMaps(const cvec_str* vs, csmap_str* mapTrans, csmap_ssv* mapSview, + cmap_str* unordmapTrans, cmap_ssv* unordmapSview) +{ + csmap_str_clear(mapTrans); + csmap_ssv_clear(mapSview); + cmap_str_clear(unordmapTrans); + cmap_ssv_clear(unordmapSview); + + size_t n = 0; + c_foreach (i, cvec_str, *vs) + { + csmap_str_insert(mapTrans, cstr_clone(*i.ref), n); + csmap_ssv_insert(mapSview, cstr_clone(*i.ref), n); + cmap_str_insert(unordmapTrans, cstr_clone(*i.ref), n); + cmap_ssv_insert(unordmapSview, cstr_clone(*i.ref), n); + ++n; + } +} + +void benchmark( + const cvec_str* vec_string, + const cvec_sv* vec_stringview, + const csmap_str* mapTrans, + const csmap_ssv* mapSview, + const cmap_str* unordmapTrans, + const cmap_ssv* unordmapSview); + +//const size_t MAX_LOOP = 1000000; +const size_t MAX_LOOP = 2000; + +int main() +{ + c_auto (cvec_str, vec_string) + c_auto (cvec_sv, vec_stringview) + c_auto (csmap_str, mapTrans) + c_auto (csmap_ssv, mapSview) + c_auto (cmap_str, unordmapTrans) + c_auto (cmap_ssv, unordmapSview) + { + std::cout << "Short String Benchmark" << std::endl; + std::cout << "======================" << std::endl; + + initShortStringVec(&vec_string, &vec_stringview); + initMaps(&vec_string, &mapTrans, &mapSview, + &unordmapTrans, &unordmapSview); + + for (int i=0; i<3; ++i) + benchmark( + &vec_string, + &vec_stringview, + &mapTrans, + &mapSview, + &unordmapTrans, + &unordmapSview); + + std::cout << "Long String Benchmark" << std::endl; + std::cout << "=====================" << std::endl; + + initLongStringVec(&vec_string, &vec_stringview); + initMaps(&vec_string, &mapTrans, &mapSview, + &unordmapTrans, &unordmapSview); + for (int i=0; i<3; ++i) + benchmark( + &vec_string, + &vec_stringview, + &mapTrans, + &mapSview, + &unordmapTrans, + &unordmapSview); + } + return 0; +} + +void benchmark( + const cvec_str* vec_string, + const cvec_sv* vec_stringview, + const csmap_str* mapTrans, + const csmap_ssv* mapSview, + const cmap_str* unordmapTrans, + const cmap_ssv* unordmapSview) +{ + size_t grandtotal = 0; + + size_t total = 0; + + timer stopwatch; + total = 0; + stopwatch.start("Trans Map with char*"); + for (size_t i = 0; i < MAX_LOOP; ++i) + { + c_foreach (j, cvec_str, *vec_string) + { + const csmap_str_value* v = csmap_str_get(mapTrans, cstr_str(j.ref)); + if (v) + total += v->second; + } + } + grandtotal += total; + stopwatch.stop(); + + total = 0; + stopwatch.start("Trans Map with string_view"); + for (size_t i = 0; i < MAX_LOOP; ++i) + { + c_foreach (j, cvec_sv, *vec_stringview) + { + const csmap_ssv_value* v = csmap_ssv_get(mapSview, *j.ref); + if (v) + total += v->second; + } + } + grandtotal += total; + stopwatch.stop(); + + total = 0; + stopwatch.start("Trans Unord Map with char*"); + for (size_t i = 0; i < MAX_LOOP; ++i) + { + c_foreach (j, cvec_str, *vec_string) + { + const cmap_str_value* v = cmap_str_get(unordmapTrans, cstr_str(j.ref)); + if (v) + total += v->second; + } + } + grandtotal += total; + stopwatch.stop(); + + total = 0; + stopwatch.start("Trans Unord Map with string_view"); + for (size_t i = 0; i < MAX_LOOP; ++i) + { + c_foreach (j, cvec_sv, *vec_stringview) + { + const cmap_ssv_value* v = cmap_ssv_get(unordmapSview, *j.ref); + if (v) + total += v->second; + } + } + grandtotal += total; + stopwatch.stop(); + + std::cout << "grandtotal:" << grandtotal << " <--- Ignore this\n" << std::endl; + +} diff --git a/misc/benchmarks/misc/string_bench_STD.cpp b/misc/benchmarks/misc/string_bench_STD.cpp new file mode 100644 index 00000000..8bb87937 --- /dev/null +++ b/misc/benchmarks/misc/string_bench_STD.cpp @@ -0,0 +1,371 @@ +// https://www.codeproject.com/Tips/5255442/Cplusplus14-20-Heterogeneous-Lookup-Benchmark +// https://github.com/shaovoon/cpp_hetero_lookup_bench +// Requires c++20, e.g. g++ -std=c++20 + +#include <iostream> +#include <iomanip> +#include <chrono> +#include <string> +#include <string_view> +#include <vector> +#include <map> +#include <unordered_map> +#define i_static +#include <stc/cstr.h> + +std::vector<std::string> read_file(const char* name) +{ + std::vector<std::string> data; + c_auto (cstr, line) + c_with (FILE* f = fopen(name, "r"), fclose(f)) + while (cstr_getline(&line, f)) + data.emplace_back(cstr_str(&line)); + return data; +} + +class timer +{ +public: + timer() = default; + void start(const std::string& text_) + { + text = text_; + begin = std::chrono::high_resolution_clock::now(); + } + void stop() + { + auto end = std::chrono::high_resolution_clock::now(); + auto dur = end - begin; + auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(dur).count(); + std::cout << std::setw(32) << text << " timing:" << std::setw(5) << ms << "ms" << std::endl; + } + +private: + std::string text; + std::chrono::high_resolution_clock::time_point begin; +}; + +void initShortStringVec(std::vector<std::string>& vs, std::vector<std::string_view>& vsv) +{ + vs.clear(); + vsv.clear(); + + vs = read_file("names.txt"); +/* + vs.push_back("Susan"); + vs.push_back("Jason"); + vs.push_back("Lily"); + vs.push_back("Michael"); + vs.push_back("Mary"); + + vs.push_back("Jerry"); + vs.push_back("Jenny"); + vs.push_back("Klaus"); + vs.push_back("Celine"); + vs.push_back("Kenny"); + + vs.push_back("Kelly"); + vs.push_back("Jackson"); + vs.push_back("Mandy"); + vs.push_back("Terry"); + vs.push_back("Sandy"); + + vs.push_back("Billy"); + vs.push_back("Cindy"); + vs.push_back("Phil"); + vs.push_back("Lindy"); + vs.push_back("David"); +*/ + size_t num = 0; + for (size_t i = 0; i < vs.size(); ++i) + { + vsv.push_back(vs.at(i)); + num += vs.at(i).size(); + } + std::cout << "num strings: " << vsv.size() << std::endl; + std::cout << "avg str len: " << num / (float)vsv.size() << std::endl; +} + +void initLongStringVec(std::vector<std::string>& vs, std::vector<std::string_view>& vsv) +{ + vs.clear(); + vsv.clear(); + + vs = read_file("names.txt"); + for (size_t i = 1; i < vs.size(); ++i) { + vs[i] += vs[i]; + vs[i] += vs[i]; + vs[i] += vs[i]; + } +/* + vs.push_back("Susan Susan Susan Susan Susan Susan"); + vs.push_back("Jason Jason Jason Jason Jason Jason"); + vs.push_back("Lily Lily Lily Lily Lily Lily"); + vs.push_back("Michael Michael Michael Michael Michael Michael"); + vs.push_back("Mary Mary Mary Mary Mary Mary"); + + vs.push_back("Jerry Jerry Jerry Jerry Jerry Jerry"); + vs.push_back("Jenny Jenny Jenny Jenny Jenny Jenny"); + vs.push_back("Klaus Klaus Klaus Klaus Klaus Klaus"); + vs.push_back("Celine Celine Celine Celine Celine Celine"); + vs.push_back("Kenny Kenny Kenny Kenny Kenny Kenny"); + + vs.push_back("Kelly Kelly Kelly Kelly Kelly Kelly"); + vs.push_back("Jackson Jackson Jackson Jackson Jackson Jackson"); + vs.push_back("Mandy Mandy Mandy Mandy Mandy Mandy"); + vs.push_back("Terry Terry Terry Terry Terry Terry"); + vs.push_back("Sandy Sandy Sandy Sandy Sandy Sandy"); + + vs.push_back("Billy Billy Billy Billy Billy Billy"); + vs.push_back("Cindy Cindy Cindy Cindy Cindy Cindy"); + vs.push_back("Phil Phil Phil Phil Phil Phil"); + vs.push_back("Lindy Lindy Lindy Lindy Lindy Lindy"); + vs.push_back("David David David David David David"); +*/ + size_t num = 0; + for (size_t i = 0; i < vs.size(); ++i) + { + vsv.push_back(vs.at(i)); + num += vs.at(i).size(); + } + std::cout << "num strings: " << vsv.size() << std::endl; + std::cout << "avg str len: " << num / (float)vsv.size() << std::endl; +} + +void initMapNormal(const std::vector<std::string>& vs, std::map<std::string, size_t>& mapNormal) +{ + mapNormal.clear(); + for (size_t i = 0; i < vs.size(); ++i) + { + mapNormal.insert(std::make_pair(vs.at(i), i)); + } +} + +void initMapTrans(const std::vector<std::string>& vs, std::map<std::string, size_t, std::less<> >& mapTrans) +{ + mapTrans.clear(); + for (size_t i = 0; i < vs.size(); ++i) + { + mapTrans.insert(std::make_pair(vs.at(i), i)); + } +} + +struct MyEqual : public std::equal_to<> +{ + using is_transparent = void; +}; + +struct string_hash { + using is_transparent = void; + using key_equal = std::equal_to<>; // Pred to use + using hash_type = std::hash<std::string_view>; // just a helper local type + size_t operator()(std::string_view txt) const { return hash_type{}(txt); } + size_t operator()(const std::string& txt) const { return hash_type{}(txt); } + size_t operator()(const char* txt) const { return hash_type{}(txt); } +}; + +void initUnorderedMapNormal(const std::vector<std::string>& vs, std::unordered_map<std::string, size_t>& unordmapNormal) +{ + unordmapNormal.clear(); + for (size_t i = 0; i < vs.size(); ++i) + { + unordmapNormal.insert(std::make_pair(vs.at(i), i)); + } +} + +void initUnorderedMapTrans(const std::vector<std::string>& vs, std::unordered_map<std::string, size_t, string_hash, MyEqual>& unordmapTrans) +{ + unordmapTrans.clear(); + for (size_t i = 0; i < vs.size(); ++i) + { + unordmapTrans.insert(std::make_pair(vs.at(i), i)); + } +} + +void benchmark( + const std::vector<std::string>& vec_shortstr, + const std::vector<std::string_view>& vec_shortstrview, + const std::map<std::string, size_t>& mapNormal, + const std::map<std::string, size_t, std::less<> >& mapTrans, + const std::unordered_map<std::string, size_t>& unordmapNormal, + const std::unordered_map<std::string, size_t, string_hash, MyEqual>& unordmapTrans); + +//const size_t MAX_LOOP = 1000000; +const size_t MAX_LOOP = 2000; + +int main() +{ + std::vector<std::string> vec_shortstr; + std::vector<std::string_view> vec_shortstrview; + + std::map<std::string, size_t> mapNormal; + std::map<std::string, size_t, std::less<> > mapTrans; + initShortStringVec(vec_shortstr, vec_shortstrview); + initMapNormal(vec_shortstr, mapNormal); + initMapTrans(vec_shortstr, mapTrans); + + std::unordered_map<std::string, size_t> unordmapNormal; + std::unordered_map<std::string, size_t, string_hash, MyEqual> unordmapTrans; + initUnorderedMapNormal(vec_shortstr, unordmapNormal); + initUnorderedMapTrans(vec_shortstr, unordmapTrans); + + std::cout << "Short String Benchmark" << std::endl; + std::cout << "======================" << std::endl; + + for (int i=0; i<3; ++i) benchmark( + vec_shortstr, + vec_shortstrview, + mapNormal, + mapTrans, + unordmapNormal, + unordmapTrans); + + std::cout << "Long String Benchmark" << std::endl; + std::cout << "=====================" << std::endl; + + initLongStringVec(vec_shortstr, vec_shortstrview); + initMapNormal(vec_shortstr, mapNormal); + initMapTrans(vec_shortstr, mapTrans); + + initUnorderedMapNormal(vec_shortstr, unordmapNormal); + initUnorderedMapTrans(vec_shortstr, unordmapTrans); + + for (int i=0; i<3; ++i) benchmark( + vec_shortstr, + vec_shortstrview, + mapNormal, + mapTrans, + unordmapNormal, + unordmapTrans); + + return 0; +} + +void benchmark( + const std::vector<std::string>& vec_shortstr, + const std::vector<std::string_view>& vec_shortstrview, + const std::map<std::string, size_t>& mapNormal, + const std::map<std::string, size_t, std::less<> >& mapTrans, + const std::unordered_map<std::string, size_t>& unordmapNormal, + const std::unordered_map<std::string, size_t, string_hash, MyEqual>& unordmapTrans) +{ + size_t grandtotal = 0; + size_t total = 0; + timer stopwatch; +/* + total = 0; + stopwatch.start("Normal Map with string"); + for (size_t i = 0; i < MAX_LOOP; ++i) + { + for (size_t j = 0; j < vec_shortstr.size(); ++j) + { + const auto& it = mapNormal.find(vec_shortstr[j]); + if(it!=mapNormal.cend()) + total += it->second; + } + } + grandtotal += total; + stopwatch.stop(); + + total = 0; + stopwatch.start("Normal Map with char*"); + for (size_t i = 0; i < MAX_LOOP; ++i) + { + for (size_t j = 0; j < vec_shortstr.size(); ++j) + { + const auto& it = mapNormal.find(vec_shortstr[j].c_str()); + if (it != mapNormal.cend()) + total += it->second; + } + } + grandtotal += total; + stopwatch.stop(); +*/ + total = 0; + stopwatch.start("Trans Map with char*"); + for (size_t i = 0; i < MAX_LOOP; ++i) + { + for (size_t j = 0; j < vec_shortstr.size(); ++j) + { + const auto& it = mapTrans.find(vec_shortstr[j].c_str()); + if (it != mapTrans.cend()) + total += it->second; + } + } + grandtotal += total; + stopwatch.stop(); + + total = 0; + stopwatch.start("Trans Map with string_view"); + for (size_t i = 0; i < MAX_LOOP; ++i) + { + for (size_t j = 0; j < vec_shortstrview.size(); ++j) + { + const auto& it = mapTrans.find(vec_shortstrview[j]); + if (it != mapTrans.cend()) + total += it->second; + } + } + grandtotal += total; + stopwatch.stop(); +/* + total = 0; + stopwatch.start("Normal Unord Map with string"); + for (size_t i = 0; i < MAX_LOOP; ++i) + { + for (size_t j = 0; j < vec_shortstr.size(); ++j) + { + const auto& it = unordmapNormal.find(vec_shortstr[j]); + if (it != unordmapNormal.cend()) + total += it->second; + } + } + grandtotal += total; + stopwatch.stop(); + + total = 0; + stopwatch.start("Normal Unord Map with char*"); + for (size_t i = 0; i < MAX_LOOP; ++i) + { + for (size_t j = 0; j < vec_shortstr.size(); ++j) + { + const auto& it = unordmapNormal.find(vec_shortstr[j].c_str()); + if (it != unordmapNormal.cend()) + total += it->second; + } + } + grandtotal += total; + stopwatch.stop(); +*/ + total = 0; + stopwatch.start("Trans Unord Map with char*"); + for (size_t i = 0; i < MAX_LOOP; ++i) + { + for (size_t j = 0; j < vec_shortstr.size(); ++j) + { + const auto& it = unordmapTrans.find(vec_shortstr[j].c_str()); + if (it != unordmapTrans.cend()) + total += it->second; + } + } + grandtotal += total; + stopwatch.stop(); + + total = 0; + stopwatch.start("Trans Unord Map with string_view"); + for (size_t i = 0; i < MAX_LOOP; ++i) + { + for (size_t j = 0; j < vec_shortstrview.size(); ++j) + { + const auto& it = unordmapTrans.find(vec_shortstrview[j]); + if (it != unordmapTrans.cend()) + total += it->second; + } + } + grandtotal += total; + + stopwatch.stop(); + + std::cout << "grandtotal:" << grandtotal << " <--- Ignore this\n" << std::endl; + +} diff --git a/misc/benchmarks/picobench/picobench.hpp b/misc/benchmarks/picobench/picobench.hpp new file mode 100644 index 00000000..2e4541e0 --- /dev/null +++ b/misc/benchmarks/picobench/picobench.hpp @@ -0,0 +1,1479 @@ +// picobench v2.00 +// https://github.com/iboB/picobench +// +// A micro microbenchmarking library in a single header file +// +// MIT License +// +// Copyright(c) 2017-2018 Borislav Stanimirov +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// +// VERSION HISTORY +// +// 2.x1 (2021-01-19) * Added user data per iter, changed text output. +// 2.01 (2019-03-03) * Fixed android build when binding to a signle core +// 2.00 (2018-10-30) * Breaking change! runner::run_benchmarks doesn't return +// a report anymore. The report is generated by +// runner::generate_report instead +// * Breaking change! report_output_format doesn't accept +// output streams as arguments. Use set_output_streams. +// * Potentially breaking change (gcc and clang)! Always set +// thread affinity to first core. Macro to turn this off. +// * Added runner::run which performs a full execution +// * Added benchmark results and results comparison +// * Added error enum +// * Macro option to allow a std::function as a benchmark +// * Macros for default iterations and samples +// * Allowing local registration of benchmarks in a runner +// * Added local_runner which doesn't consume registry +// * More force-inline functions in states +// * Fixed some potential compilation warnings +// * Removed tests from header +// * Anonymous namespace for impl-only classes and funcs +// * Added setters and getters for every config option +// 1.05 (2018-07-17) * Counting iterations of state +// * Optionally set thread affinity when running benchmarks +// so as not to miss cpu cycles with the high res clock +// 1.04 (2018-02-06) * User data for benchmarks, which can be seen from states +// * `add_custom_duration` to states so the user can modify time +// * Text table format fixes +// * Custom cmd opts in runner +// * --version CLI command +// 1.03 (2018-01-05) Added helper methods for easier browsing of reports +// 1.02 (2018-01-04) Added parsing of command line +// 1.01 (2018-01-03) * Only taking the fastest sample into account +// * Set default number of samples to 2 +// * Added CSV output +// 1.00 (2018-01-01) Initial release +// 0.01 (2017-12-28) Initial prototype release +// +// +// EXAMPLE +// +// void my_function(); // the function you want to benchmark +// +// // write your benchmarking code in a function like this +// static void benchmark_my_function(picobench::state& state) +// { +// // use the state in a range-based for loop to call your code +// for (auto _ : state) +// my_function(); +// } +// // create a picobench with your benchmarking code +// PICOBENCH(benchmark_my_function); +// +// +// BASIC DOCUMENTATION +// +// A very brief usage guide follows. For more detailed documentation see the +// README here: https://github.com/iboB/picobench/blob/master/README.md +// +// Simply include this file wherever you need. +// You need to define PICOBENCH_IMPLEMENT_WITH_MAIN (or PICOBENCH_IMPLEMENT if +// you want to write your own main function) in one compilation unit to have +// the implementation compiled there. +// +// The benchmark code must be a `void (picobench::state&)` function which +// you have written. Benchmarks are registered using the `PICOBENCH` macro +// where the only argument is the function's name. +// +// You can have multiple benchmarks in multiple files. All will be run when the +// executable starts. +// +// Typically a benchmark has a loop. To run the loop use the state argument in +// a range-based for loop in your function. The time spent looping is measured +// for the benchmark. You can have initialization/deinitialization code outside +// of the loop and it won't be measured. +// +#pragma once + +#include <cstdint> +#include <chrono> +#include <vector> + +#if defined(PICOBENCH_STD_FUNCTION_BENCHMARKS) +# include <functional> +#endif + +#define PICOBENCH_VERSION 2.x1 +#define PICOBENCH_VERSION_STR "2.x1" + +#if defined(PICOBENCH_DEBUG) +# include <cassert> +# define I_PICOBENCH_ASSERT assert +#else +# define I_PICOBENCH_ASSERT(...) +#endif + +#if defined(__GNUC__) +# define PICOBENCH_INLINE __attribute__((always_inline)) +#elif defined(_MSC_VER) +# define PICOBENCH_INLINE __forceinline +#else +# define PICOBENCH_INLINE inline +#endif + +namespace picobench +{ + +#if defined(_MSC_VER) || defined(__MINGW32__) || defined(PICOBENCH_TEST) +struct high_res_clock +{ + typedef long long rep; + typedef std::nano period; + typedef std::chrono::duration<rep, period> duration; + typedef std::chrono::time_point<high_res_clock> time_point; + static const bool is_steady = true; + + static time_point now(); +}; +#else +using high_res_clock = std::chrono::high_resolution_clock; +#endif + +using result_t = intptr_t; +using udata_t = uintptr_t; + +class state +{ +public: + explicit state(size_t num_iterations, udata_t user_data = 0, udata_t arg = 0) + : _user_data(user_data) + , _arg(arg) + , _iterations(num_iterations) + { + I_PICOBENCH_ASSERT(_iterations > 0); + } + + size_t iterations() const { return _iterations; } + + uint64_t duration_ns() const { return _duration_ns; } + void add_custom_duration(uint64_t duration_ns) { _duration_ns += duration_ns; } + + udata_t user_data() const { return _user_data; } + udata_t arg() const { return _arg; } + + // optionally set result of benchmark + // this can be used as a value sync to prevent optimizations + // or a way to check whether benchmarks produce the same results + void set_result(uintptr_t data) { _result = data; } + result_t result() const { return _result; } + + PICOBENCH_INLINE + void start_timer() + { + _start = high_res_clock::now(); + } + + PICOBENCH_INLINE + void stop_timer() + { + auto duration = high_res_clock::now() - _start; + _duration_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count(); + } + + struct iterator + { + PICOBENCH_INLINE + iterator(state* parent) + : _counter(0) + , _lim(parent->iterations()) + , _state(parent) + { + I_PICOBENCH_ASSERT(_counter < _lim); + } + + PICOBENCH_INLINE + iterator() + : _counter(0) + , _lim(0) + , _state(nullptr) + {} + + PICOBENCH_INLINE + iterator& operator++() + { + I_PICOBENCH_ASSERT(_counter < _lim); + ++_counter; + return *this; + } + + PICOBENCH_INLINE + bool operator!=(const iterator&) const + { + if (_counter < _lim) return true; + _state->stop_timer(); + return false; + } + + PICOBENCH_INLINE + size_t operator*() const + { + return _counter; + } + + private: + size_t _counter; + const size_t _lim; + state* _state; + }; + + PICOBENCH_INLINE + iterator begin() + { + start_timer(); + return iterator(this); + } + + PICOBENCH_INLINE + iterator end() + { + return iterator(); + } + +private: + high_res_clock::time_point _start; + uint64_t _duration_ns = 0; + udata_t _user_data; + udata_t _arg; + size_t _iterations; + result_t _result = 0; +}; + +// this can be used for manual measurement +class scope +{ +public: + PICOBENCH_INLINE + scope(state& s) + : _state(s) + { + _state.start_timer(); + } + + PICOBENCH_INLINE + ~scope() + { + _state.stop_timer(); + } +private: + state& _state; +}; + +#if defined(PICOBENCH_STD_FUNCTION_BENCHMARKS) +using benchmark_proc = std::function<void(state&)>; +#else +using benchmark_proc = void(*)(state&); +#endif + +class benchmark +{ +public: + const char* name() const { return _name; } + + benchmark& iterations(std::vector<size_t> data) { _state_iterations = std::move(data); return *this; } + benchmark& samples(int n) { _samples = n; return *this; } + benchmark& label(const char* label) { _name = label; return *this; } + benchmark& baseline(bool b = true) { _baseline = b; return *this; } + benchmark& user_data(udata_t data) { _user_data = data; return *this; } + benchmark& args(std::vector<udata_t> data) { _args = std::move(data); return *this; } + +protected: + friend class runner; + + benchmark(const char* name, benchmark_proc proc); + + const char* _name; + const benchmark_proc _proc; + bool _baseline = false; + + udata_t _user_data = 0; + std::vector<size_t> _state_iterations; + std::vector<udata_t> _args; + int _samples = 0; +}; + +// used for globally functions +// note that you can instantiate a runner and register local benchmarks for it alone +class global_registry +{ +public: + static int set_bench_suite(const char* name); + static benchmark& new_benchmark(const char* name, benchmark_proc proc); +}; + +} + +#define I_PICOBENCH_PP_CAT(a, b) I_PICOBENCH_PP_INTERNAL_CAT(a, b) +#define I_PICOBENCH_PP_INTERNAL_CAT(a, b) a##b + +#define PICOBENCH_SUITE(name) \ + static int I_PICOBENCH_PP_CAT(picobench_suite, __LINE__) = \ + picobench::global_registry::set_bench_suite(name) + +#define PICOBENCH(func) \ + static auto& I_PICOBENCH_PP_CAT(picobench, __LINE__) = \ + picobench::global_registry::new_benchmark(#func, func) + +#if defined(PICOBENCH_IMPLEMENT_WITH_MAIN) +# define PICOBENCH_IMPLEMENT +# define PICOBENCH_IMPLEMENT_MAIN +#endif + +#if defined(PICOBENCH_IMPLEMENT) + +#include <random> +#include <iostream> +#include <fstream> +#include <iomanip> +#include <map> +#include <memory> +#include <cstring> +#include <cstdlib> + +#if defined(_WIN32) +# define WIN32_LEAN_AND_MEAN +# include <Windows.h> +#else +# if !defined(PICOBENCH_DONT_BIND_TO_ONE_CORE) +# if defined(__APPLE__) +# include <mach/mach.h> +# else +# include <sched.h> +# endif +# endif +#endif + +namespace picobench +{ + +// namespace +// { + +enum error_t +{ + no_error, + error_bad_cmd_line_argument, // ill-formed command-line argument + error_unknown_cmd_line_argument, // command argument looks like a picobench one, but isn't + error_sample_compare, // benchmark produced different results across samples + error_benchmark_compare, // two benchmarks of the same suite and dimension produced different results +}; + +class report +{ +public: + struct benchmark_problem_space + { + size_t dimension; // number of iterations for the problem space + udata_t user_data; + udata_t arg; + int samples; // number of samples taken + uint64_t total_time_ns; // fastest sample!!! + result_t result; // result of fastest sample + }; + struct benchmark + { + const char* name; + bool is_baseline; + std::vector<benchmark_problem_space> data; + }; + + struct suite + { + const char* name; + std::vector<benchmark> benchmarks; // benchmark view + + const benchmark* find_benchmark(const char* name) const + { + for (auto& b : benchmarks) + { + if (strcmp(b.name, name) == 0) + return &b; + } + + return nullptr; + } + + const benchmark* find_baseline() const + { + for (auto& b : benchmarks) + { + if (b.is_baseline) + return &b; + } + + return nullptr; + } + }; + + std::vector<suite> suites; + error_t error = no_error; + + const suite* find_suite(const char* name) const + { + for (auto& s : suites) + { + if (strcmp(s.name, name) == 0) + return &s; + } + + return nullptr; + } + + void to_text(std::ostream& out) const + { + using namespace std; + int width = 100; + for (auto& suite : suites) + { + if (suite.name) + { + out << suite.name << ":\n"; + } + line(out, width); + out << + " Name (* = baseline) |Iterations | Arg |Baseline | ns/op | Total ms | Ops/second\n"; + line(out, width); + + auto problem_space_view = get_problem_space_view(suite); + for (auto& ps : problem_space_view) + { + const problem_space_benchmark* baseline = nullptr; + for (auto& bm : ps.second) + { + if (bm.is_baseline) + { + baseline = &bm; + break; + } + } + + for (auto& bm : ps.second) + { + out << (bm.is_baseline ? "* " : " ") << left << setw(26) << bm.name << right; + + out << " |" + << setw(10) << ps.first.first << " |" + << setw(10) << bm.arg << " |"; + + if (bm.is_baseline) //(baseline == &bm) + { + baseline = &bm; + out << " - |"; + } + else if (baseline) + { + out << setw(8) << fixed << setprecision(3) + << double(bm.total_time_ns) / double(baseline->total_time_ns) << " |"; + } + else + { + // no baseline to compare to + out << " ? |"; + } + + auto ns_op = (bm.total_time_ns / ps.first.first); + if (ns_op > 99999999) + { + int e = 0; + while (ns_op > 999999) + { + ++e; + ns_op /= 10; + } + out << setw(8) << ns_op << 'e' << e; + } + else + { + out << setw(10) << ns_op; + } + out << " |"; + out << setw(10) << fixed << setprecision(2) << double(bm.total_time_ns) / 1000000.0 << " |"; + + auto ops_per_sec = ps.first.first * (1000000000.0 / double(bm.total_time_ns)); + out << setw(12) << fixed << setprecision(1) << ops_per_sec << "\n"; + } + } + line(out, width); + } + } + + void to_text_concise(std::ostream& out) + { + using namespace std; + int width = 65; + for (auto& suite : suites) + { + if (suite.name) + { + out << suite.name << ":\n"; + } + + line(out, width); + + out << + " Name (* = baseline) | Baseline | ns/op | Total ms\n"; // | Ops/second\n"; + + line(out, width); + + const benchmark* baseline = nullptr; + for (auto& bm : suite.benchmarks) + { + if (bm.is_baseline) + { + baseline = &bm; + break; + } + } + I_PICOBENCH_ASSERT(baseline); + uint64_t baseline_total_time = 0; + size_t baseline_total_iterations = 0; + for (auto& d : baseline->data) + { + baseline_total_time += d.total_time_ns; + baseline_total_iterations += d.dimension; + } + + for (auto& bm : suite.benchmarks) + { + out << (bm.is_baseline ? "* " : " ") << left << setw(26) << bm.name << right + << " |"; + + uint64_t total_time = 0; + size_t total_iterations = 0; + for (auto& d : bm.data) + { + total_time += d.total_time_ns; + total_iterations += d.dimension; + } + uint64_t ns_per_op = total_time / total_iterations; + + if (bm.is_baseline) + { + out << " -"; + baseline = &bm; + baseline_total_time = total_time; + baseline_total_iterations = total_iterations; + } + else + { + out << setw(9) << fixed << setprecision(3) + << double(total_time) / baseline_total_time; + } + + out << " |" << setw(10) << ns_per_op << " |"; + out << setw(12) << fixed << setprecision(2) << double(total_time) / 1000000.0 << "\n"; + + //auto ops_per_sec = total_iterations * (1000000000.0 / total_time); + //out << setw(12) << fixed << setprecision(1) << ops_per_sec << "\n"; + } + + line(out, width); + } + } + + void to_csv(std::ostream& out) const + { + using namespace std; + const char* sep = ","; + + for (auto& suite : suites) + { + out << "Suite, Baseline, Benchmark, Iterations, Arg, Ratio, Total ms, ns/op, Ops/second\n"; + + auto problem_space_view = get_problem_space_view(suite); + for (auto& ps : problem_space_view) + { + const problem_space_benchmark* baseline = nullptr; + for (auto& bm : ps.second) + { + if (bm.is_baseline) + { + baseline = &bm; + break; + } + } + + for (auto& bm : ps.second) + { + out << '"' << (suite.name ? suite.name : "") << '"'; + out << sep << (bm.is_baseline ? "true" : "false"); + out << sep << '"' << bm.name << '"'; + out << sep << ps.first.first + << sep << bm.arg << sep; + + if (baseline == &bm) + { + out << 1.0; + } + else if (baseline) + { + out << fixed << setprecision(3) << double(bm.total_time_ns) / baseline->total_time_ns; + } + else + { + out << -1.0; // no baseline to compare to + } + + out << sep << fixed << setprecision(3) << bm.total_time_ns / 1000000.0; + + auto ns_op = (bm.total_time_ns / ps.first.first); + out << sep << ns_op; + + auto ops_per_sec = ps.first.first * (1000000000.0 / bm.total_time_ns); + out << sep << fixed << setprecision(1) << ops_per_sec << "\n"; + } + } + } + } + + + + struct problem_space_benchmark + { + const char* name; + bool is_baseline; + udata_t user_data; + udata_t arg; + uint64_t total_time_ns; // fastest sample!!! + result_t result; // result of fastest sample + }; + using problem_space_view_map = std::map<std::pair<size_t, udata_t>, + std::vector<problem_space_benchmark>>; + static problem_space_view_map get_problem_space_view(const suite& s) + { + problem_space_view_map res; + for (auto& bm : s.benchmarks) + { + for (auto& d : bm.data) + { + auto& pvbs = res[{d.dimension, d.arg}]; + pvbs.push_back({ bm.name, bm.is_baseline, d.user_data, d.arg, d.total_time_ns, d.result }); + } + } + return res; + } + +private: + + static void line(std::ostream& out, int width = 79) + { + for (int i = 0; i < width; ++i) out.put('='); + out.put('\n'); + } +}; + +class benchmark_impl : public benchmark +{ +public: + benchmark_impl(const char* name, benchmark_proc proc) + : benchmark(name, proc) + {} + +private: + friend class runner; + + // state + std::vector<state> _states; // length is _samples * _state_iterations.size() + std::vector<state>::iterator _istate; +}; + +class picostring +{ +public: + picostring() = default; + explicit picostring(const char* text) + { + str = text; + len = int(strlen(text)); + } + + const char* str; + int len = 0; + + // checks whether other begins with this string + bool cmp(const char* other) const + { + return strncmp(str, other, size_t(len)) == 0; + } +}; + +class null_streambuf : public std::streambuf +{ +public: + virtual int overflow(int c) override { return c; } +}; + +struct null_stream : public std::ostream +{ + null_stream() : std::ostream(&_buf) {} +private: + null_streambuf _buf; +} cnull; + +enum class report_output_format +{ + text, + concise_text, + csv, + all, +}; + +#if !defined(PICOBENCH_DEFAULT_ITERATIONS) +# define PICOBENCH_DEFAULT_ITERATIONS { 8, 64, 512, 4096, 8192 } +#endif + +#if !defined(PICOBENCH_DEFAULT_SAMPLES) +# define PICOBENCH_DEFAULT_SAMPLES 2 +#endif + +using benchmarks_vector = std::vector<std::unique_ptr<benchmark_impl>>; +struct rsuite +{ + const char* name; + benchmarks_vector benchmarks; +}; + +class registry +{ +public: + benchmark& add_benchmark(const char* name, benchmark_proc proc) + { + auto b = new benchmark_impl(name, proc); + benchmarks_for_current_suite().emplace_back(b); + return *b; + } + + void set_suite(const char* name) + { + _current_suite_name = name; + } + + const char*& current_suite_name() + { + return _current_suite_name; + } + + benchmarks_vector& benchmarks_for_current_suite() + { + for (auto& s : _suites) + { + if (s.name == _current_suite_name) + return s.benchmarks; + + if (s.name && _current_suite_name && strcmp(s.name, _current_suite_name) == 0) + return s.benchmarks; + } + _suites.push_back({ _current_suite_name, {} }); + return _suites.back().benchmarks; + } + +protected: + friend class runner; + const char* _current_suite_name = nullptr; + std::vector<rsuite> _suites; +}; + +registry& g_registry() +{ + static registry r; + return r; +} + +class runner : public registry +{ +public: + runner(bool local = false) + : _default_state_iterations(PICOBENCH_DEFAULT_ITERATIONS) + , _default_samples(PICOBENCH_DEFAULT_SAMPLES) + { + if (!local) + { + _suites = std::move(g_registry()._suites); + } + } + + int run(int benchmark_random_seed = -1) + { + if (should_run()) + { + run_benchmarks(benchmark_random_seed); + auto report = generate_report(); + std::ostream* out = _stdout; + std::ofstream fout; + report_output_format fmt[] = {report_output_format::csv, + report_output_format::text, + report_output_format::concise_text}; + const char *ext[] = {".csv", ".txt", ".lst"}, *fn = preferred_output_filename(); + bool all = preferred_output_format() == report_output_format::all; + for (int i = 0; i < 3; ++i) + { + if (all || preferred_output_format() == fmt[i]) + { + if (fn) + { + std::string name(fn); + + if (all || name.find(".") == std::string::npos) + { + name += ext[i]; + } + fout.close(); + fout.open(name.c_str()); + if (!fout.is_open()) + { + std::cerr << "Error: Could not open output file `" << fn << "`\n"; + return 1; + } + out = &fout; + } + + switch (fmt[i]) + { + case report_output_format::text: + report.to_text(*out); + break; + case report_output_format::concise_text: + report.to_text_concise(*out); + break; + case report_output_format::csv: + report.to_csv(*out); + break; + default: break; + } + } + } + } + return error(); + } + + void run_benchmarks(int random_seed = -1) + { + I_PICOBENCH_ASSERT(_error == no_error && _should_run); + + if (random_seed == -1) + { + random_seed = int(std::random_device()()); + } + + std::minstd_rand rnd(random_seed); + + // vector of all benchmarks + std::vector<benchmark_impl*> benchmarks; + for (auto& suite : _suites) + { + // also identify a baseline in this loop + // if there is no explicit one, set the first one as a baseline + bool found_baseline = false; + for (auto irb = suite.benchmarks.begin(); irb != suite.benchmarks.end(); ++irb) + { + auto& rb = *irb; + rb->_states.clear(); // clear states so we can safely call run_benchmarks multiple times + benchmarks.push_back(rb.get()); + if (rb->_baseline) + { + found_baseline = true; + } + +#if !defined(PICOBENCH_STD_FUNCTION_BENCHMARKS) + // check for same func + for (auto ib = irb+1; ib != suite.benchmarks.end(); ++ib) + { + auto& b = *ib; + if (rb->_proc == b->_proc) + { + *_stdwarn << "Warning: " << rb->name() << " and " << b->name() + << " are benchmarks of the same function.\n"; + } + } +#endif + } + + if (!found_baseline && !suite.benchmarks.empty()) + { + suite.benchmarks.front()->_baseline = true; + } + } + + // initialize benchmarks + for (auto b : benchmarks) + { + if (b->_state_iterations.empty()) + b->_state_iterations = _default_state_iterations; + + udata_t arg = b->_args.empty() ? udata_t() : b->_args.back(); + b->_args.resize(b->_state_iterations.size(), arg); + + if (b->_samples == 0) + b->_samples = _default_samples; + + b->_states.reserve(b->_state_iterations.size() * b->_samples); + + // fill states while random shuffling them + for (size_t iter = 0; iter < b->_state_iterations.size(); ++iter) + { + for (int i = 0; i < b->_samples; ++i) + { + auto index = rnd() % (b->_states.size() + 1); + auto pos = b->_states.begin() + long(index); + b->_states.emplace(pos, b->_state_iterations[iter], b->_user_data, b->_args[iter]); + } + } + + b->_istate = b->_states.begin(); + } + +#if !defined(PICOBENCH_DONT_BIND_TO_ONE_CORE) + // set thread affinity to first cpu + // so the high resolution clock doesn't miss cycles + { +#if defined(_WIN32) + SetThreadAffinityMask(GetCurrentThread(), 1); +#elif defined(__APPLE__) + thread_affinity_policy_data_t policy = {0}; + thread_policy_set( + pthread_mach_thread_np(pthread_self()), + THREAD_AFFINITY_POLICY, + (thread_policy_t)&policy, 1); +#else + cpu_set_t cpuset; + CPU_ZERO(&cpuset); + CPU_SET(0, &cpuset); + + sched_setaffinity(0, sizeof(cpu_set_t), &cpuset); +#endif + } +#endif + + // we run a random benchmark from it incrementing _istate for each + // when _istate reaches _states.end(), we erase the benchmark + // when the vector becomes empty, we're done + while (!benchmarks.empty()) + { + auto i = benchmarks.begin() + long(rnd() % benchmarks.size()); + auto& b = *i; + std::cerr << '.'; + b->_proc(*b->_istate); + + ++b->_istate; + + if (b->_istate == b->_states.end()) + { + benchmarks.erase(i); + } + } + std::cerr << '\n'; + } + + // function to compare results + template <typename CompareResult = std::equal_to<result_t>> + report generate_report(CompareResult cmp = std::equal_to<result_t>()) const + { + report rpt; + + rpt.suites.resize(_suites.size()); + auto rpt_suite = rpt.suites.begin(); + + for (auto& suite : _suites) + { + rpt_suite->name = suite.name; + + // build benchmark view + rpt_suite->benchmarks.resize(suite.benchmarks.size()); + auto rpt_benchmark = rpt_suite->benchmarks.begin(); + + for (auto& b : suite.benchmarks) + { + rpt_benchmark->name = b->_name; + rpt_benchmark->is_baseline = b->_baseline; + + rpt_benchmark->data.reserve(b->_state_iterations.size()); + for (size_t i = 0; i < b->_state_iterations.size(); ++i) + { + rpt_benchmark->data.push_back({ b->_state_iterations[i], b->_user_data, b->_args[i], 0, 0ll }); + } + + for (auto& state : b->_states) + { + for (auto& d : rpt_benchmark->data) + { + if (state.iterations() == d.dimension && state.arg() == d.arg) + { + if (d.total_time_ns == 0 || d.total_time_ns > state.duration_ns()) + { + d.total_time_ns = state.duration_ns(); + d.result = state.result(); + } + + if (_compare_results_across_samples) + { + if (d.result != state.result() && !cmp(d.result, state.result())) + { + *_stderr << "Error: Two samples of " << b->name() << " @" << d.dimension << " produced different results: " + << d.result << " and " << state.result() << '\n'; + _error = error_sample_compare; + } + } + + ++d.samples; + } + } + } + +#if defined(PICOBENCH_DEBUG) + for (auto& d : rpt_benchmark->data) + { + I_PICOBENCH_ASSERT(d.samples == b->_samples); + } +#endif + + ++rpt_benchmark; + } + + ++rpt_suite; + } + + if (_compare_results_across_benchmarks) + { + for(auto& suite : rpt.suites) + { + auto psview = report::get_problem_space_view(suite); + + for (auto& space : psview) + { + I_PICOBENCH_ASSERT(!space.second.empty()); + + if (space.second.size() == 1) + { + auto& b = space.second.front(); + *_stdwarn << "Warning: Benchmark " << b.name << " @" << space.first.first + << " has a single instance and cannot be compared to others.\n"; + continue; + } + + auto result0 = space.second.front().result; + + for (auto& b : space.second) + { + if (result0 != b.result && !cmp(result0, b.result)) + { + auto& f = space.second.front(); + *_stderr << "Error: Benchmarks " << f.name << " and " << b.name + << " @" << space.first.first << " produce different results: " + << result0 << " and " << b.result << '\n'; + _error = error_benchmark_compare; + } + } + } + } + } + + return rpt; + } + + void set_default_state_iterations(const std::vector<size_t>& data) + { + _default_state_iterations = data; + } + + const std::vector<size_t>& default_state_iterations() const + { + return _default_state_iterations; + } + + void set_default_samples(int n) + { + _default_samples = n; + } + + int default_samples() const + { + return _default_samples; + } + + void add_cmd_opt(const char* cmd, const char* arg_desc, const char* cmd_desc, bool(*handler)(uintptr_t, const char*), udata_t user_data = 0) + { + cmd_line_option opt; + opt.cmd = picostring(cmd); + opt.arg_desc = picostring(arg_desc); + opt.desc = cmd_desc; + opt.handler = nullptr; + opt.user_data = user_data; + opt.user_handler = handler; + _opts.push_back(opt); + } + + // returns false if there were errors parsing the command line + // all args starting with prefix are parsed + // the others are ignored + bool parse_cmd_line(int argc, const char* const argv[], const char* cmd_prefix = "-") + { + _cmd_prefix = picostring(cmd_prefix); + + if (!_has_opts) + { + _opts.emplace_back("-iters=", "<n1,n2,n3,...>", + "Sets default iterations for benchmarks", + &runner::cmd_iters); + _opts.emplace_back("-samples=", "<n>", + "Sets default number of samples for benchmarks", + &runner::cmd_samples); + _opts.emplace_back("-out-fmt=", "<txt|con|csv>", + "Outputs text, concise, csv or all", + &runner::cmd_out_fmt); + _opts.emplace_back("-all", "", + "Outputs all formats: text, con, csv", + &runner::cmd_out_all); + _opts.emplace_back("-output=", "<filename>", + "Sets output filename or `stdout`", + &runner::cmd_output); + _opts.emplace_back("-no-compare-results", "", + "Doesn't compare benchmark results", + &runner::cmd_compare_results); + _opts.emplace_back("-no-run", "", + "Doesn't run benchmarks", + &runner::cmd_no_run); + _opts.emplace_back("-version", "", + "Show version info", + &runner::cmd_version); + _opts.emplace_back("-help", "", + "Prints help", + &runner::cmd_help); + _has_opts = true; + } + + for (int i = 1; i < argc; ++i) + { + if (!_cmd_prefix.cmp(argv[i])) + continue; + + auto arg = argv[i] + _cmd_prefix.len; + + bool found = false; + for (auto& opt : _opts) + { + if (opt.cmd.cmp(arg)) + { + found = true; + bool success = false; + if (opt.handler) + { + success = (this->*opt.handler)(arg + opt.cmd.len); + } + else + { + I_PICOBENCH_ASSERT(opt.user_handler); + success = opt.user_handler(opt.user_data, arg + opt.cmd.len); + } + + if (!success) + { + *_stderr << "Error: Bad command-line argument: " << argv[i] << "\n"; + _error = error_bad_cmd_line_argument; + return false; + } + break; + } + } + + if (!found) + { + *_stderr << "Error: Unknown command-line argument: " << argv[i] << "\n"; + _error = error_unknown_cmd_line_argument; + return false; + } + } + + return true; + } + + void set_should_run(bool set) { _should_run = set; } + bool should_run() const { return _error == no_error && _should_run; } + void set_error(error_t e) { _error = e; } + error_t error() const { return _error; } + + void set_output_streams(std::ostream& out, std::ostream& err) + { + _stdout = &out; + _stderr = &err; + _stdwarn = &out; + } + + void set_preferred_output_format(report_output_format fmt) { _output_format = fmt; } + report_output_format preferred_output_format() const { return _output_format; } + + // can be nullptr (run will interpret it as stdout) + void set_preferred_output_filename(const char* path) { _output_file = path; } + const char* preferred_output_filename() const { return _output_file; } + + void set_compare_results_across_samples(bool b) { _compare_results_across_samples = b; } + bool compare_results_across_samples() const { return _compare_results_across_samples; } + + void set_compare_results_across_benchmarks(bool b) { _compare_results_across_benchmarks = b; } + bool compare_results_across_benchmarks() const { return _compare_results_across_benchmarks; } + +private: + // runner's suites and benchmarks come from its parent: registry + + // state and configuration + mutable error_t _error = no_error; + bool _should_run = true; + + bool _compare_results_across_samples = true; + bool _compare_results_across_benchmarks = true; + + report_output_format _output_format = report_output_format::concise_text; + const char* _output_file = nullptr; // nullptr means stdout + + std::ostream* _stdout = &std::cout; + std::ostream* _stderr = &std::cerr; + std::ostream* _stdwarn = &std::cout; + + // default data + + // default iterations per state per benchmark + std::vector<size_t> _default_state_iterations; + + // default samples per benchmark + int _default_samples; + + // command line parsing + picostring _cmd_prefix; + typedef bool (runner::*cmd_handler)(const char*); // internal handler + typedef bool(*ext_handler)(udata_t user_data, const char* cmd_line); // external (user) handler + struct cmd_line_option + { + cmd_line_option() = default; + cmd_line_option(const char* c, const char* a, const char* d, cmd_handler h) + : cmd(c) + , arg_desc(a) + , desc(d) + , handler(h) + , user_data(0) + , user_handler(nullptr) + {} + picostring cmd; + picostring arg_desc; + const char* desc; + cmd_handler handler; // may be nullptr for external handlers + udata_t user_data; // passed as an argument to user handlers + ext_handler user_handler; + }; + bool _has_opts = false; // have opts been added to list + std::vector<cmd_line_option> _opts; + + bool cmd_iters(const char* line) + { + std::vector<size_t> iters; + auto p = line; + while (true) + { + auto i = strtoull(p, nullptr, 10); + if (i <= 0) return false; + iters.push_back(i); + p = strchr(p + 1, ','); + if (!p) break; + ++p; + } + if (iters.empty()) return false; + _default_state_iterations = iters; + return true; + } + + bool cmd_samples(const char* line) + { + int samples = int(strtol(line, nullptr, 10)); + if (samples <= 0) return false; + _default_samples = samples; + return true; + } + + bool cmd_no_run(const char* line) + { + if (*line) return false; + _should_run = false; + return true; + } + + bool cmd_version(const char* line) + { + if (*line) return false; + *_stdout << "picobench " PICOBENCH_VERSION_STR << "\n"; + _should_run = false; + return true; + } + + bool cmd_help(const char* line) + { + if (*line) return false; + cmd_version(line); + auto& cout = *_stdout; + for (auto& opt : _opts) + { + cout << ' ' << _cmd_prefix.str << opt.cmd.str << opt.arg_desc.str; + int w = 27 - (_cmd_prefix.len + opt.cmd.len + opt.arg_desc.len); + for (int i = 0; i < w; ++i) + { + cout.put(' '); + } + cout << opt.desc << "\n"; + } + _should_run = false; + return true; + } + + bool cmd_out_all(const char* line) + { + _output_format = report_output_format::all; + return true; + } + bool cmd_out_fmt(const char* line) + { + if (strcmp(line, "txt") == 0) + { + _output_format = report_output_format::text; + } + else if (strcmp(line, "con") == 0) + { + _output_format = report_output_format::concise_text; + } + else if (strcmp(line, "csv") == 0) + { + _output_format = report_output_format::csv; + } + else + { + return false; + } + return true; + } + + bool cmd_output(const char* line) + { + if (strcmp(line, "stdout") != 0) + { + _output_file = line; + } + else + { + _output_file = nullptr; + } + return true; + } + + bool cmd_compare_results(const char* line) + { + if (*line) return false; + _compare_results_across_samples = false; + _compare_results_across_benchmarks = false; + return true; + } +}; + +class local_runner : public runner +{ +public: + local_runner() : runner(true) + {} +}; + +// } // anonymous namespace + +benchmark::benchmark(const char* name, benchmark_proc proc) + : _name(name) + , _proc(proc) +{} + +benchmark& global_registry::new_benchmark(const char* name, benchmark_proc proc) +{ + return g_registry().add_benchmark(name, proc); +} + +int global_registry::set_bench_suite(const char* name) +{ + g_registry().current_suite_name() = name; + return 0; +} + +#if (defined(_MSC_VER) || defined(__MINGW32__)) && !defined(PICOBENCH_TEST) + +static const long long high_res_clock_freq = []() -> long long +{ + LARGE_INTEGER frequency; + QueryPerformanceFrequency(&frequency); + return frequency.QuadPart; +}(); + +high_res_clock::time_point high_res_clock::now() +{ + LARGE_INTEGER t; + QueryPerformanceCounter(&t); + return time_point(duration((t.QuadPart * rep(period::den)) / high_res_clock_freq)); +} +#endif +} + +#endif + +#if defined(PICOBENCH_IMPLEMENT_MAIN) +int main(int argc, char* argv[]) +{ + picobench::runner r; + r.parse_cmd_line(argc, argv); + return r.run(); +} +#endif + +#if defined(PICOBENCH_TEST) + +// fake time keeping functions for the tests +namespace picobench +{ + +void this_thread_sleep_for_ns(uint64_t ns); + +template <class Rep, class Period> +void this_thread_sleep_for(const std::chrono::duration<Rep, Period>& duration) +{ + this_thread_sleep_for_ns(std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count()); +} + +#if defined(PICOBENCH_IMPLEMENT) +static struct fake_time +{ + uint64_t now; +} the_time; + +void this_thread_sleep_for_ns(uint64_t ns) +{ + the_time.now += ns; +} + +high_res_clock::time_point high_res_clock::now() +{ + auto ret = time_point(duration(the_time.now)); + return ret; +} +#endif + +} + +#endif diff --git a/misc/benchmarks/picobench/picobench_cmap.cpp b/misc/benchmarks/picobench/picobench_cmap.cpp new file mode 100644 index 00000000..3ffba5b9 --- /dev/null +++ b/misc/benchmarks/picobench/picobench_cmap.cpp @@ -0,0 +1,284 @@ +#define i_static +#include <stc/crandom.h> +#define i_static +#include <stc/cstr.h> +#include <cmath> +#include <string> +#include <unordered_map> +#include <stdexcept> +#include "../external/ankerl/unordered_dense.h" +#include "../external/skarupke/flat_hash_map.hpp" +#include "../external/tsl/robin_map.h" + +#define PICOBENCH_IMPLEMENT_WITH_MAIN +#include "picobench.hpp" + +enum {N1 = 4000000, S1 = 1, MaxLoadFactor100 = 80}; +uint64_t seed = time(NULL); + +template <class K, class V> using umap = std::unordered_map<K, V>; +template <class K, class V> using fmap = ska::flat_hash_map<K, V>; +template <class K, class V> using tmap = tsl::robin_map<K, V>; +template <class K, class V> using dmap = ankerl::unordered_dense::map<K, V>; +#define DEFMAP(map, ...) \ + using u##map = umap __VA_ARGS__; \ + using f##map = fmap __VA_ARGS__; \ + using t##map = tmap __VA_ARGS__; \ + using d##map = dmap __VA_ARGS__ + +DEFMAP(map_i, <int32_t, int32_t>); +DEFMAP(map_x, <uint64_t, uint64_t>); +DEFMAP(map_s, <std::string, std::string>); + +#define i_key int32_t +#define i_val int32_t +#define i_tag i +#define i_max_load_factor float(MaxLoadFactor100) / 100.0f +#include <stc/cmap.h> + +#define i_key uint64_t +#define i_val uint64_t +#define i_tag x +#define i_max_load_factor float(MaxLoadFactor100) / 100.0f +#include <stc/cmap.h> + +#define i_key_str +#define i_val_str +#define i_max_load_factor float(MaxLoadFactor100) / 100.0f +#include <stc/cmap.h> + +PICOBENCH_SUITE("Map1"); + +template <class MapInt> +static void ins_and_erase_i(picobench::state& s) +{ + MapInt map; + map.max_load_factor((int)MaxLoadFactor100 / 100.0); + csrandom(seed); + + picobench::scope scope(s); + c_forrange (s.iterations()) + map[crandom()]; + map.clear(); + csrandom(seed); + c_forrange (s.iterations()) + map[crandom()]; + csrandom(seed); + c_forrange (s.iterations()) + map.erase(crandom()); + s.set_result(map.size()); +} +/* +static void ins_and_erase_cmap_i(picobench::state& s) +{ + cmap_i map = cmap_i_init(); + csrandom(seed); + + picobench::scope scope(s); + c_forrange (s.iterations()) + cmap_i_insert(&map, crandom(), 0); + cmap_i_clear(&map); + csrandom(seed); + c_forrange (s.iterations()) + cmap_i_insert(&map, crandom(), 0); + csrandom(seed); + c_forrange (s.iterations()) + cmap_i_erase(&map, crandom()); + s.set_result(cmap_i_size(&map)); + cmap_i_drop(&map); +} +*/ +static void ins_and_erase_cmap_x(picobench::state& s) +{ + cmap_x map = cmap_x_init(); + csrandom(seed); + + picobench::scope scope(s); + c_forrange (s.iterations()) + cmap_x_insert(&map, crandom(), 0); + cmap_x_clear(&map); + csrandom(seed); + c_forrange (s.iterations()) + cmap_x_insert(&map, crandom(), 0); + csrandom(seed); + c_forrange (s.iterations()) + cmap_x_erase(&map, crandom()); + s.set_result(cmap_x_size(&map)); + cmap_x_drop(&map); +} + +#define P samples(S1).iterations({N1/4}) +PICOBENCH(ins_and_erase_i<umap_x>).P; +PICOBENCH(ins_and_erase_i<dmap_x>).P; +PICOBENCH(ins_and_erase_i<fmap_x>).P; +PICOBENCH(ins_and_erase_i<tmap_x>).P; +PICOBENCH(ins_and_erase_cmap_x).P; +#undef P + +PICOBENCH_SUITE("Map2"); + +template <class MapInt> +static void ins_and_access_i(picobench::state& s) +{ + uint64_t mask = (1ull << s.arg()) - 1; + size_t result = 0; + MapInt map; + map.max_load_factor((int)MaxLoadFactor100 / 100.0); + csrandom(seed); + + picobench::scope scope(s); + c_forrange (N1) + result += ++map[crandom() & mask]; + s.set_result(result); +} + +static void ins_and_access_cmap_i(picobench::state& s) +{ + uint64_t mask = (1ull << s.arg()) - 1; + size_t result = 0; + cmap_i map = cmap_i_init(); + csrandom(seed); + + picobench::scope scope(s); + c_forrange (N1) + result += ++cmap_i_insert(&map, crandom() & mask, 0).ref->second; + s.set_result(result); + cmap_i_drop(&map); +} + +#define P samples(S1).iterations({N1, N1, N1, N1}).args({18, 23, 25, 31}) +PICOBENCH(ins_and_access_i<umap_i>).P; +PICOBENCH(ins_and_access_i<dmap_i>).P; +PICOBENCH(ins_and_access_i<fmap_i>).P; +PICOBENCH(ins_and_access_i<tmap_i>).P; +PICOBENCH(ins_and_access_cmap_i).P; +#undef P + +PICOBENCH_SUITE("Map3"); + +static void randomize(char* str, size_t len) { + for (size_t k=0; k < len; ++k) { + union {uint64_t i; char c[8];} r = {.i = crandom()}; + for (unsigned i=0; i<8 && k<len; ++k, ++i) + str[k] = (r.c[i] & 63) + 48; + } +} + +template <class MapStr> +static void ins_and_access_s(picobench::state& s) +{ + std::string str(s.arg(), 'x'); + size_t result = 0; + MapStr map; + map.max_load_factor((int)MaxLoadFactor100 / 100.0); + csrandom(seed); + + picobench::scope scope(s); + c_forrange (s.iterations()) { + randomize(&str[0], str.size()); + map.emplace(str, str); + randomize(&str[0], str.size()); + result += map.erase(str); + } + s.set_result(result + map.size()); +} + +static void ins_and_access_cmap_s(picobench::state& s) +{ + cstr str = cstr_with_size(s.arg(), 'x'); + char* buf = cstr_data(&str); + size_t result = 0; + cmap_str map = cmap_str_init(); + csrandom(seed); + + picobench::scope scope(s); + c_forrange (s.iterations()) { + randomize(buf, s.arg()); + //if (s.arg() > 30) { printf("%s\n", buf); exit(0); } + cmap_str_emplace(&map, buf, buf); + + randomize(buf, s.arg()); + result += cmap_str_erase(&map, buf); + } + s.set_result(result + cmap_str_size(&map)); + cstr_drop(&str); + cmap_str_drop(&map); +} + +#define P samples(S1).iterations({N1/5, N1/5, N1/5, N1/10, N1/40}).args({13, 7, 8, 100, 1000}) +PICOBENCH(ins_and_access_s<umap_s>).P; +PICOBENCH(ins_and_access_s<dmap_s>).P; +PICOBENCH(ins_and_access_s<fmap_s>).P; +PICOBENCH(ins_and_access_s<tmap_s>).P; +PICOBENCH(ins_and_access_cmap_s).P; +#undef P + +PICOBENCH_SUITE("Map4"); + +template <class MapX> +static void iterate_x(picobench::state& s) +{ + MapX map; + map.max_load_factor((int)MaxLoadFactor100 / 100.0); + uint64_t K = (1ull << s.arg()) - 1; + + picobench::scope scope(s); + csrandom(seed); + size_t result = 0; + + // measure insert then iterate whole map + c_forrange (n, s.iterations()) { + map[crandom()] = n; + if (!(n & K)) for (auto const& keyVal : map) + result += keyVal.second; + } + + // reset rng back to inital state + csrandom(seed); + + // measure erase then iterate whole map + c_forrange (n, s.iterations()) { + map.erase(crandom()); + if (!(n & K)) for (auto const& keyVal : map) + result += keyVal.second; + } + s.set_result(result); +} + +static void iterate_cmap_x(picobench::state& s) +{ + cmap_x map = cmap_x_init(); + uint64_t K = (1ull << s.arg()) - 1; + + picobench::scope scope(s); + csrandom(seed); + size_t result = 0; + + // measure insert then iterate whole map + c_forrange (n, s.iterations()) { + cmap_x_insert_or_assign(&map, crandom(), n); + if (!(n & K)) c_foreach (i, cmap_x, map) + result += i.ref->second; + } + + // reset rng back to inital state + csrandom(seed); + + // measure erase then iterate whole map + c_forrange (n, s.iterations()) { + cmap_x_erase(&map, crandom()); + if (!(n & K)) c_foreach (i, cmap_x, map) + result += i.ref->second; + } + s.set_result(result); + cmap_x_drop(&map); +} + + +#define P samples(S1).iterations({N1/20}).args({12}) +PICOBENCH(iterate_x<umap_x>).P; +PICOBENCH(iterate_x<dmap_x>).P; +PICOBENCH(iterate_x<fmap_x>).P; +PICOBENCH(iterate_x<tmap_x>).P; +PICOBENCH(iterate_cmap_x).P; +#undef P diff --git a/misc/benchmarks/picobench/picobench_csmap.cpp b/misc/benchmarks/picobench/picobench_csmap.cpp new file mode 100644 index 00000000..5caab6cc --- /dev/null +++ b/misc/benchmarks/picobench/picobench_csmap.cpp @@ -0,0 +1,320 @@ +#include <iostream> +#define i_static +#include <stc/crandom.h> +#define i_static +#include <stc/cstr.h> +#include <cmath> +#include <string> +#include <map> + +#define PICOBENCH_IMPLEMENT_WITH_MAIN +#include "picobench.hpp" + +enum {N1 = 1000000, S1 = 1}; +uint64_t seed = time(NULL); // 18237129837891; + +using omap_i = std::map<int, int>; +using omap_x = std::map<uint64_t, uint64_t>; +using omap_s = std::map<std::string, std::string>; + +#define i_key int +#define i_val int +#define i_tag i +#include <stc/csmap.h> + +#define i_key size_t +#define i_val size_t +#define i_tag x +#include <stc/csmap.h> + +#define i_key_str +#define i_val_str +#include <stc/csmap.h> + +PICOBENCH_SUITE("Map1"); + +template <class MapInt> +static void ctor_and_ins_one_i(picobench::state& s) +{ + size_t result = 0; + picobench::scope scope(s); + c_forrange (n, s.iterations()) { + MapInt map; + map[n]; + result += map.size(); + } + s.set_result(result); +} +/* +static void ctor_and_ins_one_csmap_i(picobench::state& s) +{ + size_t result = 0; + picobench::scope scope(s); + c_forrange (n, s.iterations()) { + csmap_i map = csmap_i_init(); + csmap_i_insert(&map, n, 0); + result += csmap_i_size(&map); + csmap_i_drop(&map); + } + s.set_result(result); +} + +#define P samples(S1).iterations({N1}) +PICOBENCH(ctor_and_ins_one_i<omap_i>).P; +PICOBENCH(ctor_and_ins_one_csmap_i).P; +#undef P +*/ + +PICOBENCH_SUITE("Map_insert_only"); + +template <class MapInt> +static void insert_i(picobench::state& s) +{ + MapInt map; + csrandom(seed); + picobench::scope scope(s); + c_forrange (n, s.iterations()) + map.emplace(crandom() & 0xfffffff, n); + s.set_result(map.size()); +} + +static void insert_csmap_i(picobench::state& s) +{ + csmap_i map = csmap_i_init(); + csrandom(seed); + picobench::scope scope(s); + c_forrange (n, s.iterations()) + csmap_i_insert(&map, crandom() & 0xfffffff, n); + s.set_result(csmap_i_size(&map)); + csmap_i_drop(&map); +} + +#define P samples(S1).iterations({N1}) +PICOBENCH(insert_i<omap_i>).P; +PICOBENCH(insert_csmap_i).P; +#undef P + + +PICOBENCH_SUITE("Map2"); + +template <class MapInt> +static void ins_and_erase_i(picobench::state& s) +{ + size_t result = 0; + uint64_t mask = (1ull << s.arg()) - 1; + MapInt map; + csrandom(seed); + + picobench::scope scope(s); + c_forrange (i, s.iterations()) + map.emplace(crandom() & mask, i); + result = map.size(); + + map.clear(); + csrandom(seed); + c_forrange (i, s.iterations()) + map[crandom() & mask] = i; + + csrandom(seed); + c_forrange (s.iterations()) + map.erase(crandom() & mask); + s.set_result(result); +} + +static void ins_and_erase_csmap_i(picobench::state& s) +{ + size_t result = 0; + uint64_t mask = (1ull << s.arg()) - 1; + csmap_i map = csmap_i_init(); + csrandom(seed); + + picobench::scope scope(s); + c_forrange (i, s.iterations()) + csmap_i_insert(&map, crandom() & mask, i); + result = csmap_i_size(&map); + + csmap_i_clear(&map); + csrandom(seed); + c_forrange (i, s.iterations()) + csmap_i_insert_or_assign(&map, crandom() & mask, i); + + csrandom(seed); + c_forrange (s.iterations()) + csmap_i_erase(&map, crandom() & mask); + s.set_result(result); + csmap_i_drop(&map); +} + +#define P samples(S1).iterations({N1/2, N1/2, N1/2, N1/2}).args({18, 23, 25, 31}) +PICOBENCH(ins_and_erase_i<omap_i>).P; +PICOBENCH(ins_and_erase_csmap_i).P; +#undef P + +PICOBENCH_SUITE("Map3"); + +template <class MapInt> +static void ins_and_access_i(picobench::state& s) +{ + uint64_t mask = (1ull << s.arg()) - 1; + size_t result = 0; + MapInt map; + csrandom(seed); + + picobench::scope scope(s); + c_forrange (s.iterations()) { + result += ++map[crandom() & mask]; + auto it = map.find(crandom() & mask); + if (it != map.end()) map.erase(it->first); + } + s.set_result(result + map.size()); +} + +static void ins_and_access_csmap_i(picobench::state& s) +{ + uint64_t mask = (1ull << s.arg()) - 1; + size_t result = 0; + csmap_i map = csmap_i_init(); + csrandom(seed); + + picobench::scope scope(s); + c_forrange (s.iterations()) { + result += ++csmap_i_insert(&map, crandom() & mask, 0).ref->second; + const csmap_i_value* val = csmap_i_get(&map, crandom() & mask); + if (val) csmap_i_erase(&map, val->first); + } + s.set_result(result + csmap_i_size(&map)); + csmap_i_drop(&map); +} + +#define P samples(S1).iterations({N1, N1, N1, N1}).args({18, 23, 25, 31}) +PICOBENCH(ins_and_access_i<omap_i>).P; +PICOBENCH(ins_and_access_csmap_i).P; +#undef P + +PICOBENCH_SUITE("Map4"); + +static void randomize(char* str, int len) { + union {uint64_t i; char c[8];} r = {.i = crandom()}; + for (int i = len - 7, j = 0; i < len; ++j, ++i) + str[i] = (r.c[j] & 63) + 48; +} + +template <class MapStr> +static void ins_and_access_s(picobench::state& s) +{ + std::string str(s.arg(), 'x'); + size_t result = 0; + MapStr map; + + picobench::scope scope(s); + csrandom(seed); + c_forrange (s.iterations()) { + randomize(&str[0], str.size()); + map.emplace(str, str); + } + csrandom(seed); + c_forrange (s.iterations()) { + randomize(&str[0], str.size()); + result += map.erase(str); + } + s.set_result(result + map.size()); +} + +static void ins_and_access_csmap_s(picobench::state& s) +{ + cstr str = cstr_with_size(s.arg(), 'x'); + char* buf = cstr_data(&str); + size_t result = 0; + csmap_str map = csmap_str_init(); + + picobench::scope scope(s); + csrandom(seed); + c_forrange (s.iterations()) { + randomize(buf, s.arg()); + csmap_str_emplace(&map, buf, buf); + } + csrandom(seed); + c_forrange (s.iterations()) { + randomize(buf, s.arg()); + result += csmap_str_erase(&map, buf); + /*csmap_str_iter it = csmap_str_find(&map, buf); + if (it.ref) { + ++result; + csmap_str_erase(&map, cstr_str(&it.ref->first)); + }*/ + } + s.set_result(result + csmap_str_size(&map)); + cstr_drop(&str); + csmap_str_drop(&map); +} + +#define P samples(S1).iterations({N1/5, N1/5, N1/5, N1/10, N1/40}).args({13, 7, 8, 100, 1000}) +PICOBENCH(ins_and_access_s<omap_s>).P; +PICOBENCH(ins_and_access_csmap_s).P; +#undef P + +PICOBENCH_SUITE("Map5"); + +template <class MapX> +static void iterate_x(picobench::state& s) +{ + MapX map; + uint64_t K = (1ull << s.arg()) - 1; + + picobench::scope scope(s); + csrandom(seed); + size_t result = 0; + + // measure insert then iterate whole map + c_forrange (n, s.iterations()) { + map[crandom()] = n; + if (!(n & K)) for (auto const& keyVal : map) + result += keyVal.second; + } + + // reset rng back to inital state + csrandom(seed); + + // measure erase then iterate whole map + c_forrange (n, s.iterations()) { + map.erase(crandom()); + if (!(n & K)) for (auto const& keyVal : map) + result += keyVal.second; + } + s.set_result(result); +} +/* +static void iterate_csmap_x(picobench::state& s) +{ + csmap_x map = csmap_x_init(); + uint64_t K = (1ull << s.arg()) - 1; + + picobench::scope scope(s); + csrandom(seed); + size_t result = 0; + + // measure insert then iterate whole map + c_forrange (n, s.iterations()) { + csmap_x_insert_or_assign(&map, crandom(), n); + if (!(n & K)) c_foreach (i, csmap_x, map) + result += i.ref->second; + } + + // reset rng back to inital state + csrandom(seed); + + // measure erase then iterate whole map + c_forrange (n, s.iterations()) { + csmap_x_erase(&map, crandom()); + if (!(n & K)) c_foreach (i, csmap_x, map) + result += i.ref->second; + } + s.set_result(result); + csmap_x_drop(&map); +} + +#define P samples(S1).iterations({N1/20}).args({12}) +PICOBENCH(iterate_x<omap_x>).P; +PICOBENCH(iterate_csmap_x).P; +#undef P +*/
\ No newline at end of file diff --git a/misc/benchmarks/pics/benchmark.gif b/misc/benchmarks/pics/benchmark.gif Binary files differnew file mode 100644 index 00000000..90f47fff --- /dev/null +++ b/misc/benchmarks/pics/benchmark.gif diff --git a/misc/benchmarks/plotbench/cdeq_benchmark.cpp b/misc/benchmarks/plotbench/cdeq_benchmark.cpp new file mode 100644 index 00000000..1259cc07 --- /dev/null +++ b/misc/benchmarks/plotbench/cdeq_benchmark.cpp @@ -0,0 +1,138 @@ +#include <stdio.h> +#include <time.h> +#define i_static +#include <stc/crandom.h> + +#ifdef __cplusplus +#include <deque> +#include <algorithm> +#endif + +enum {INSERT, ERASE, FIND, ITER, DESTRUCT, N_TESTS}; +const char* operations[] = {"insert", "erase", "find", "iter", "destruct"}; +typedef struct { time_t t1, t2; uint64_t sum; float fac; } Range; +typedef struct { const char* name; Range test[N_TESTS]; } Sample; +enum {SAMPLES = 2, N = 100000000, S = 0x3ffc, R = 4}; +uint64_t seed = 1, mask1 = 0xfffffff, mask2 = 0xffff; + +static float secs(Range s) { return (float)(s.t2 - s.t1) / CLOCKS_PER_SEC; } + +#define i_tag x +#define i_val size_t +#include <stc/cdeq.h> + +#ifdef __cplusplus +Sample test_std_deque() { + typedef std::deque<size_t> container; + Sample s = {"std,deque"}; + { + s.test[INSERT].t1 = clock(); + container con; + csrandom(seed); + c_forrange (N/3) con.push_front(crandom() & mask1); + c_forrange (N/3) {con.push_back(crandom() & mask1); con.pop_front();} + c_forrange (N/3) con.push_back(crandom() & mask1); + s.test[INSERT].t2 = clock(); + s.test[INSERT].sum = con.size(); + s.test[ERASE].t1 = clock(); + c_forrange (con.size()/2) { con.pop_front(); con.pop_back(); } + s.test[ERASE].t2 = clock(); + s.test[ERASE].sum = con.size(); + }{ + container con; + csrandom(seed); + c_forrange (N) con.push_back(crandom() & mask2); + s.test[FIND].t1 = clock(); + size_t sum = 0; + // Iteration - not inherent find - skipping + //container::iterator it; + //c_forrange (S) if ((it = std::find(con.begin(), con.end(), crandom() & mask2)) != con.end()) sum += *it; + s.test[FIND].t2 = clock(); + s.test[FIND].sum = sum; + s.test[ITER].t1 = clock(); + sum = 0; + c_forrange (R) c_forrange (i, N) sum += con[i]; + s.test[ITER].t2 = clock(); + s.test[ITER].sum = sum; + s.test[DESTRUCT].t1 = clock(); + } + s.test[DESTRUCT].t2 = clock(); + s.test[DESTRUCT].sum = 0; + return s; +} +#else +Sample test_std_deque() { Sample s = {"std-deque"}; return s;} +#endif + + +Sample test_stc_deque() { + typedef cdeq_x container; + Sample s = {"STC,deque"}; + { + s.test[INSERT].t1 = clock(); + container con = cdeq_x_init(); + //cdeq_x_reserve(&con, N); + csrandom(seed); + c_forrange (N/3) cdeq_x_push_front(&con, crandom() & mask1); + c_forrange (N/3) {cdeq_x_push_back(&con, crandom() & mask1); cdeq_x_pop_front(&con);} + c_forrange (N/3) cdeq_x_push_back(&con, crandom() & mask1); + s.test[INSERT].t2 = clock(); + s.test[INSERT].sum = cdeq_x_size(&con); + s.test[ERASE].t1 = clock(); + c_forrange (cdeq_x_size(&con)/2) { cdeq_x_pop_front(&con); cdeq_x_pop_back(&con); } + s.test[ERASE].t2 = clock(); + s.test[ERASE].sum = cdeq_x_size(&con); + cdeq_x_drop(&con); + }{ + csrandom(seed); + container con = cdeq_x_init(); + c_forrange (N) cdeq_x_push_back(&con, crandom() & mask2); + s.test[FIND].t1 = clock(); + size_t sum = 0; + //cdeq_x_iter it, end = cdeq_x_end(&con); + //c_forrange (S) if ((it = cdeq_x_find(&con, crandom() & mask2)).ref != end.ref) sum += *it.ref; + s.test[FIND].t2 = clock(); + s.test[FIND].sum = sum; + s.test[ITER].t1 = clock(); + sum = 0; + c_forrange (R) c_forrange (i, N) sum += con.data[i]; + s.test[ITER].t2 = clock(); + s.test[ITER].sum = sum; + s.test[DESTRUCT].t1 = clock(); + cdeq_x_drop(&con); + } + s.test[DESTRUCT].t2 = clock(); + s.test[DESTRUCT].sum = 0; + return s; +} + +int main(int argc, char* argv[]) +{ + Sample std_s[SAMPLES + 1], stc_s[SAMPLES + 1]; + c_forrange (i, SAMPLES) { + std_s[i] = test_std_deque(); + stc_s[i] = test_stc_deque(); + if (i > 0) c_forrange (j, N_TESTS) { + if (secs(std_s[i].test[j]) < secs(std_s[0].test[j])) std_s[0].test[j] = std_s[i].test[j]; + if (secs(stc_s[i].test[j]) < secs(stc_s[0].test[j])) stc_s[0].test[j] = stc_s[i].test[j]; + if (stc_s[i].test[j].sum != stc_s[0].test[j].sum) printf("Error in sum: test %lld, sample %lld\n", i, j); + } + } + const char* comp = argc > 1 ? argv[1] : "test"; + bool header = (argc > 2 && argv[2][0] == '1'); + float std_sum = 0, stc_sum = 0; + + c_forrange (j, N_TESTS) { + std_sum += secs(std_s[0].test[j]); + stc_sum += secs(stc_s[0].test[j]); + } + if (header) printf("Compiler,Library,C,Method,Seconds,Ratio\n"); + + c_forrange (j, N_TESTS) + printf("%s,%s n:%d,%s,%.3f,%.3f\n", comp, std_s[0].name, N, operations[j], secs(std_s[0].test[j]), 1.0f); + printf("%s,%s n:%d,%s,%.3f,%.3f\n", comp, std_s[0].name, N, "total", std_sum, 1.0f); + + c_forrange (j, N_TESTS) + printf("%s,%s n:%d,%s,%.3f,%.3f\n", comp, stc_s[0].name, N, operations[j], secs(stc_s[0].test[j]), secs(std_s[0].test[j]) ? secs(stc_s[0].test[j])/secs(std_s[0].test[j]) : 1.0f); + printf("%s,%s n:%d,%s,%.3f,%.3f\n", comp, stc_s[0].name, N, "total", stc_sum, stc_sum/std_sum); +} diff --git a/misc/benchmarks/plotbench/clist_benchmark.cpp b/misc/benchmarks/plotbench/clist_benchmark.cpp new file mode 100644 index 00000000..04c8e8cd --- /dev/null +++ b/misc/benchmarks/plotbench/clist_benchmark.cpp @@ -0,0 +1,135 @@ +#include <stdio.h> +#include <time.h> +#define i_static +#include <stc/crandom.h> + +#ifdef __cplusplus +#include <forward_list> +#include <algorithm> +#endif + +enum {INSERT, ERASE, FIND, ITER, DESTRUCT, N_TESTS}; +const char* operations[] = {"insert", "erase", "find", "iter", "destruct"}; +typedef struct { time_t t1, t2; uint64_t sum; float fac; } Range; +typedef struct { const char* name; Range test[N_TESTS]; } Sample; +enum {SAMPLES = 2, N = 50000000, S = 0x3ffc, R = 4}; +uint64_t seed = 1, mask1 = 0xfffffff, mask2 = 0xffff; + +static float secs(Range s) { return (float)(s.t2 - s.t1) / CLOCKS_PER_SEC; } + +#define i_val size_t +#define i_tag x +#include <stc/clist.h> + +#ifdef __cplusplus +Sample test_std_forward_list() { + typedef std::forward_list<size_t> container; + Sample s = {"std,forward_list"}; + { + s.test[INSERT].t1 = clock(); + container con; + csrandom(seed); + c_forrange (N/2) con.push_front(crandom() & mask1); + c_forrange (N/2) con.push_front(crandom() & mask1); + s.test[INSERT].t2 = clock(); + s.test[INSERT].sum = 0; + s.test[ERASE].t1 = clock(); + c_forrange (N) con.pop_front(); + s.test[ERASE].t2 = clock(); + s.test[ERASE].sum = 0; + }{ + container con; + csrandom(seed); + c_forrange (N) con.push_front(crandom() & mask2); + s.test[FIND].t1 = clock(); + size_t sum = 0; + container::iterator it; + // Iteration - not inherent find - skipping + //c_forrange (S) if ((it = std::find(con.begin(), con.end(), crandom() & mask2)) != con.end()) sum += *it; + s.test[FIND].t2 = clock(); + s.test[FIND].sum = sum; + s.test[ITER].t1 = clock(); + sum = 0; + c_forrange (R) for (auto i: con) sum += i; + s.test[ITER].t2 = clock(); + s.test[ITER].sum = sum; + s.test[DESTRUCT].t1 = clock(); + } + s.test[DESTRUCT].t2 = clock(); + s.test[DESTRUCT].sum = 0; + return s; +} +#else +Sample test_std_forward_list() { Sample s = {"std-forward_list"}; return s;} +#endif + + +Sample test_stc_forward_list() { + typedef clist_x container; + Sample s = {"STC,forward_list"}; + { + s.test[INSERT].t1 = clock(); + container con = clist_x_init(); + csrandom(seed); + c_forrange (N/2) clist_x_push_front(&con, crandom() & mask1); + c_forrange (N/2) clist_x_push_back(&con, crandom() & mask1); + s.test[INSERT].t2 = clock(); + s.test[INSERT].sum = 0; + s.test[ERASE].t1 = clock(); + c_forrange (N) clist_x_pop_front(&con); + s.test[ERASE].t2 = clock(); + s.test[ERASE].sum = 0; + clist_x_drop(&con); + }{ + csrandom(seed); + container con = clist_x_init(); + c_forrange (N) clist_x_push_front(&con, crandom() & mask2); + s.test[FIND].t1 = clock(); + size_t sum = 0; + //clist_x_iter it, end = clist_x_end(&con); + //c_forrange (S) if ((it = clist_x_find(&con, crandom() & mask2)).ref != end.ref) sum += *it.ref; + s.test[FIND].t2 = clock(); + s.test[FIND].sum = sum; + s.test[ITER].t1 = clock(); + sum = 0; + c_forrange (R) c_foreach (i, clist_x, con) sum += *i.ref; + s.test[ITER].t2 = clock(); + s.test[ITER].sum = sum; + s.test[DESTRUCT].t1 = clock(); + clist_x_drop(&con); + } + s.test[DESTRUCT].t2 = clock(); + s.test[DESTRUCT].sum = 0; + return s; +} + +int main(int argc, char* argv[]) +{ + Sample std_s[SAMPLES + 1], stc_s[SAMPLES + 1]; + c_forrange (i, SAMPLES) { + std_s[i] = test_std_forward_list(); + stc_s[i] = test_stc_forward_list(); + if (i > 0) c_forrange (j, N_TESTS) { + if (secs(std_s[i].test[j]) < secs(std_s[0].test[j])) std_s[0].test[j] = std_s[i].test[j]; + if (secs(stc_s[i].test[j]) < secs(stc_s[0].test[j])) stc_s[0].test[j] = stc_s[i].test[j]; + if (stc_s[i].test[j].sum != stc_s[0].test[j].sum) printf("Error in sum: test %lld, sample %lld\n", i, j); + } + } + const char* comp = argc > 1 ? argv[1] : "test"; + bool header = (argc > 2 && argv[2][0] == '1'); + float std_sum = 0, stc_sum = 0; + + c_forrange (j, N_TESTS) { + std_sum += secs(std_s[0].test[j]); + stc_sum += secs(stc_s[0].test[j]); + } + if (header) printf("Compiler,Library,C,Method,Seconds,Ratio\n"); + + c_forrange (j, N_TESTS) + printf("%s,%s n:%d,%s,%.3f,%.3f\n", comp, std_s[0].name, N, operations[j], secs(std_s[0].test[j]), 1.0f); + printf("%s,%s n:%d,%s,%.3f,%.3f\n", comp, std_s[0].name, N, "total", std_sum, 1.0f); + + c_forrange (j, N_TESTS) + printf("%s,%s n:%d,%s,%.3f,%.3f\n", comp, stc_s[0].name, N, operations[j], secs(stc_s[0].test[j]), secs(std_s[0].test[j]) ? secs(stc_s[0].test[j])/secs(std_s[0].test[j]) : 1.0f); + printf("%s,%s n:%d,%s,%.3f,%.3f\n", comp, stc_s[0].name, N, "total", stc_sum, stc_sum/std_sum); +}
\ No newline at end of file diff --git a/misc/benchmarks/plotbench/cmap_benchmark.cpp b/misc/benchmarks/plotbench/cmap_benchmark.cpp new file mode 100644 index 00000000..7a8f29d2 --- /dev/null +++ b/misc/benchmarks/plotbench/cmap_benchmark.cpp @@ -0,0 +1,142 @@ +#include <stdio.h> +#include <time.h> +#define i_static +#include <stc/crandom.h> + +#ifdef __cplusplus +#include <unordered_map> +#endif + +enum {INSERT, ERASE, FIND, ITER, DESTRUCT, N_TESTS}; +const char* operations[] = {"insert", "erase", "find", "iter", "destruct"}; +typedef struct { time_t t1, t2; uint64_t sum; float fac; } Range; +typedef struct { const char* name; Range test[N_TESTS]; } Sample; +enum {SAMPLES = 2, N = 8000000, R = 4}; +uint64_t seed = 1, mask1 = 0xffffffff; + +static float secs(Range s) { return (float)(s.t2 - s.t1) / CLOCKS_PER_SEC; } + +#define i_key uint64_t +#define i_val uint64_t +#define i_tag x +#include <stc/cmap.h> + +#ifdef __cplusplus +Sample test_std_unordered_map() { + typedef std::unordered_map<uint64_t, uint64_t> container; + Sample s = {"std,unordered_map"}; + { + csrandom(seed); + s.test[INSERT].t1 = clock(); + container con; + c_forrange (i, N/2) con.emplace(crandom() & mask1, i); + c_forrange (i, N/2) con.emplace(i, i); + s.test[INSERT].t2 = clock(); + s.test[INSERT].sum = con.size(); + csrandom(seed); + s.test[ERASE].t1 = clock(); + c_forrange (N) con.erase(crandom() & mask1); + s.test[ERASE].t2 = clock(); + s.test[ERASE].sum = con.size(); + }{ + container con; + csrandom(seed); + c_forrange (i, N/2) con.emplace(crandom() & mask1, i); + c_forrange (i, N/2) con.emplace(i, i); + csrandom(seed); + s.test[FIND].t1 = clock(); + size_t sum = 0; + container::iterator it; + c_forrange (N) if ((it = con.find(crandom() & mask1)) != con.end()) sum += it->second; + s.test[FIND].t2 = clock(); + s.test[FIND].sum = sum; + s.test[ITER].t1 = clock(); + sum = 0; + c_forrange (R) for (auto i: con) sum += i.second; + s.test[ITER].t2 = clock(); + s.test[ITER].sum = sum; + s.test[DESTRUCT].t1 = clock(); + } + s.test[DESTRUCT].t2 = clock(); + s.test[DESTRUCT].sum = 0; + return s; +} +#else +Sample test_std_unordered_map() { Sample s = {"std-unordered_map"}; return s;} +#endif + + +Sample test_stc_unordered_map() { + typedef cmap_x container; + Sample s = {"STC,unordered_map"}; + { + csrandom(seed); + s.test[INSERT].t1 = clock(); + container con = cmap_x_init(); + c_forrange (i, N/2) cmap_x_insert(&con, crandom() & mask1, i); + c_forrange (i, N/2) cmap_x_insert(&con, i, i); + s.test[INSERT].t2 = clock(); + s.test[INSERT].sum = cmap_x_size(&con); + csrandom(seed); + s.test[ERASE].t1 = clock(); + c_forrange (N) cmap_x_erase(&con, crandom() & mask1); + s.test[ERASE].t2 = clock(); + s.test[ERASE].sum = cmap_x_size(&con); + cmap_x_drop(&con); + }{ + container con = cmap_x_init(); + csrandom(seed); + c_forrange (i, N/2) cmap_x_insert(&con, crandom() & mask1, i); + c_forrange (i, N/2) cmap_x_insert(&con, i, i); + csrandom(seed); + s.test[FIND].t1 = clock(); + size_t sum = 0; + const cmap_x_value* val; + c_forrange (N) + if ((val = cmap_x_get(&con, crandom() & mask1))) + sum += val->second; + s.test[FIND].t2 = clock(); + s.test[FIND].sum = sum; + s.test[ITER].t1 = clock(); + sum = 0; + c_forrange (R) c_foreach (i, cmap_x, con) sum += i.ref->second; + s.test[ITER].t2 = clock(); + s.test[ITER].sum = sum; + s.test[DESTRUCT].t1 = clock(); + cmap_x_drop(&con); + } + s.test[DESTRUCT].t2 = clock(); + s.test[DESTRUCT].sum = 0; + return s; +} + +int main(int argc, char* argv[]) +{ + Sample std_s[SAMPLES + 1], stc_s[SAMPLES + 1]; + c_forrange (i, SAMPLES) { + std_s[i] = test_std_unordered_map(); + stc_s[i] = test_stc_unordered_map(); + if (i > 0) c_forrange (j, N_TESTS) { + if (secs(std_s[i].test[j]) < secs(std_s[0].test[j])) std_s[0].test[j] = std_s[i].test[j]; + if (secs(stc_s[i].test[j]) < secs(stc_s[0].test[j])) stc_s[0].test[j] = stc_s[i].test[j]; + if (stc_s[i].test[j].sum != stc_s[0].test[j].sum) printf("Error in sum: test %lld, sample %lld\n", i, j); + } + } + const char* comp = argc > 1 ? argv[1] : "test"; + bool header = (argc > 2 && argv[2][0] == '1'); + float std_sum = 0, stc_sum = 0; + + c_forrange (j, N_TESTS) { + std_sum += secs(std_s[0].test[j]); + stc_sum += secs(stc_s[0].test[j]); + } + if (header) printf("Compiler,Library,C,Method,Seconds,Ratio\n"); + + c_forrange (j, N_TESTS) + printf("%s,%s n:%d,%s,%.3f,%.3f\n", comp, std_s[0].name, N, operations[j], secs(std_s[0].test[j]), 1.0f); + printf("%s,%s n:%d,%s,%.3f,%.3f\n", comp, std_s[0].name, N, "total", std_sum, 1.0f); + + c_forrange (j, N_TESTS) + printf("%s,%s n:%d,%s,%.3f,%.3f\n", comp, stc_s[0].name, N, operations[j], secs(stc_s[0].test[j]), secs(std_s[0].test[j]) ? secs(stc_s[0].test[j])/secs(std_s[0].test[j]) : 1.0f); + printf("%s,%s n:%d,%s,%.3f,%.3f\n", comp, stc_s[0].name, N, "total", stc_sum, stc_sum/std_sum); +}
\ No newline at end of file diff --git a/misc/benchmarks/plotbench/cpque_benchmark.cpp b/misc/benchmarks/plotbench/cpque_benchmark.cpp new file mode 100644 index 00000000..a729c09f --- /dev/null +++ b/misc/benchmarks/plotbench/cpque_benchmark.cpp @@ -0,0 +1,71 @@ +#include <stdio.h> +#include <time.h> +#define i_static +#include <stc/crandom.h> + +#define i_val float +#define i_cmp -c_default_cmp +#define i_tag f +#include <stc/cpque.h> + +#include <queue> + +static const uint32_t seed = 1234; + +void std_test() +{ + stc64_t rng; + int N = 10000000; + + std::priority_queue<float, std::vector<float>, std::greater<float>> pq; + rng = stc64_new(seed); + clock_t start = clock(); + c_forrange (i, N) + pq.push((float) stc64_randf(&rng)*100000); + + printf("Built priority queue: %f secs\n", (clock() - start) / (float) CLOCKS_PER_SEC); + printf("%g ", pq.top()); + + start = clock(); + c_forrange (i, N) { + pq.pop(); + } + + printf("\npopped PQ: %f secs\n\n", (clock() - start) / (float) CLOCKS_PER_SEC); +} + + +void stc_test() +{ + stc64_t rng; + int N = 10000000, M = 10; + + c_auto (cpque_f, pq) + { + rng = stc64_new(seed); + clock_t start = clock(); + c_forrange (i, N) + cpque_f_push(&pq, (float) stc64_randf(&rng)*100000); + + printf("Built priority queue: %f secs\n", (clock() - start) / (float) CLOCKS_PER_SEC); + printf("%g ", *cpque_f_top(&pq)); + + c_forrange (i, M) { + cpque_f_pop(&pq); + } + + start = clock(); + c_forrange (i, M, N) + cpque_f_pop(&pq); + printf("\npopped PQ: %f secs\n", (clock() - start) / (float) CLOCKS_PER_SEC); + } +} + + +int main() +{ + puts("STD P.QUEUE:"); + std_test(); + puts("\nSTC P.QUEUE:"); + stc_test(); +} diff --git a/misc/benchmarks/plotbench/csmap_benchmark.cpp b/misc/benchmarks/plotbench/csmap_benchmark.cpp new file mode 100644 index 00000000..46bd695c --- /dev/null +++ b/misc/benchmarks/plotbench/csmap_benchmark.cpp @@ -0,0 +1,143 @@ +#include <stdio.h> +#include <time.h> +#define i_static +#include <stc/crandom.h> + +#ifdef __cplusplus +#include <map> +#endif + +enum {INSERT, ERASE, FIND, ITER, DESTRUCT, N_TESTS}; +const char* operations[] = {"insert", "erase", "find", "iter", "destruct"}; +typedef struct { time_t t1, t2; uint64_t sum; float fac; } Range; +typedef struct { const char* name; Range test[N_TESTS]; } Sample; +enum {SAMPLES = 2, N = 4000000, R = 4}; +uint64_t seed = 1, mask1 = 0xfffffff; + +static float secs(Range s) { return (float)(s.t2 - s.t1) / CLOCKS_PER_SEC; } + +#define i_key size_t +#define i_val size_t +#define i_tag x +#include <stc/csmap.h> + +#ifdef __cplusplus +Sample test_std_map() { + typedef std::map<size_t, size_t> container; + Sample s = {"std,map"}; + { + csrandom(seed); + s.test[INSERT].t1 = clock(); + container con; + c_forrange (i, N/2) con.emplace(crandom() & mask1, i); + c_forrange (i, N/2) con.emplace(i, i); + s.test[INSERT].t2 = clock(); + s.test[INSERT].sum = con.size(); + csrandom(seed); + s.test[ERASE].t1 = clock(); + c_forrange (N) con.erase(crandom() & mask1); + s.test[ERASE].t2 = clock(); + s.test[ERASE].sum = con.size(); + }{ + container con; + csrandom(seed); + c_forrange (i, N/2) con.emplace(crandom() & mask1, i); + c_forrange (i, N/2) con.emplace(i, i); + csrandom(seed); + s.test[FIND].t1 = clock(); + size_t sum = 0; + container::iterator it; + c_forrange (N) if ((it = con.find(crandom() & mask1)) != con.end()) sum += it->second; + s.test[FIND].t2 = clock(); + s.test[FIND].sum = sum; + s.test[ITER].t1 = clock(); + sum = 0; + c_forrange (R) for (auto i: con) sum += i.second; + s.test[ITER].t2 = clock(); + s.test[ITER].sum = sum; + s.test[DESTRUCT].t1 = clock(); + } + s.test[DESTRUCT].t2 = clock(); + s.test[DESTRUCT].sum = 0; + return s; +} +#else +Sample test_std_map() { Sample s = {"std-map"}; return s;} +#endif + + + +Sample test_stc_map() { + typedef csmap_x container; + Sample s = {"STC,map"}; + { + csrandom(seed); + s.test[INSERT].t1 = clock(); + container con = csmap_x_init(); + c_forrange (i, N/2) csmap_x_insert(&con, crandom() & mask1, i); + c_forrange (i, N/2) csmap_x_insert(&con, i, i); + s.test[INSERT].t2 = clock(); + s.test[INSERT].sum = csmap_x_size(&con); + csrandom(seed); + s.test[ERASE].t1 = clock(); + c_forrange (N) csmap_x_erase(&con, crandom() & mask1); + s.test[ERASE].t2 = clock(); + s.test[ERASE].sum = csmap_x_size(&con); + csmap_x_drop(&con); + }{ + container con = csmap_x_init(); + csrandom(seed); + c_forrange (i, N/2) csmap_x_insert(&con, crandom() & mask1, i); + c_forrange (i, N/2) csmap_x_insert(&con, i, i); + csrandom(seed); + s.test[FIND].t1 = clock(); + size_t sum = 0; + const csmap_x_value* val; + c_forrange (N) + if ((val = csmap_x_get(&con, crandom() & mask1))) + sum += val->second; + s.test[FIND].t2 = clock(); + s.test[FIND].sum = sum; + s.test[ITER].t1 = clock(); + sum = 0; + c_forrange (R) c_foreach (i, csmap_x, con) sum += i.ref->second; + s.test[ITER].t2 = clock(); + s.test[ITER].sum = sum; + s.test[DESTRUCT].t1 = clock(); + csmap_x_drop(&con); + } + s.test[DESTRUCT].t2 = clock(); + s.test[DESTRUCT].sum = 0; + return s; +} + +int main(int argc, char* argv[]) +{ + Sample std_s[SAMPLES + 1], stc_s[SAMPLES + 1]; + c_forrange (i, SAMPLES) { + std_s[i] = test_std_map(); + stc_s[i] = test_stc_map(); + if (i > 0) c_forrange (j, N_TESTS) { + if (secs(std_s[i].test[j]) < secs(std_s[0].test[j])) std_s[0].test[j] = std_s[i].test[j]; + if (secs(stc_s[i].test[j]) < secs(stc_s[0].test[j])) stc_s[0].test[j] = stc_s[i].test[j]; + if (stc_s[i].test[j].sum != stc_s[0].test[j].sum) printf("Error in sum: test %lld, sample %lld\n", i, j); + } + } + const char* comp = argc > 1 ? argv[1] : "test"; + bool header = (argc > 2 && argv[2][0] == '1'); + float std_sum = 0, stc_sum = 0; + + c_forrange (j, N_TESTS) { + std_sum += secs(std_s[0].test[j]); + stc_sum += secs(stc_s[0].test[j]); + } + if (header) printf("Compiler,Library,C,Method,Seconds,Ratio\n"); + + c_forrange (j, N_TESTS) + printf("%s,%s n:%d,%s,%.3f,%.3f\n", comp, std_s[0].name, N, operations[j], secs(std_s[0].test[j]), 1.0f); + printf("%s,%s n:%d,%s,%.3f,%.3f\n", comp, std_s[0].name, N, "total", std_sum, 1.0f); + + c_forrange (j, N_TESTS) + printf("%s,%s n:%d,%s,%.3f,%.3f\n", comp, stc_s[0].name, N, operations[j], secs(stc_s[0].test[j]), secs(std_s[0].test[j]) ? secs(stc_s[0].test[j])/secs(std_s[0].test[j]) : 1.0f); + printf("%s,%s n:%d,%s,%.3f,%.3f\n", comp, stc_s[0].name, N, "total", stc_sum, stc_sum/std_sum); +} diff --git a/misc/benchmarks/plotbench/cvec_benchmark.cpp b/misc/benchmarks/plotbench/cvec_benchmark.cpp new file mode 100644 index 00000000..fe7e09fb --- /dev/null +++ b/misc/benchmarks/plotbench/cvec_benchmark.cpp @@ -0,0 +1,134 @@ +#include <stdio.h> +#include <time.h> +#define i_static +#include <stc/crandom.h> + +#ifdef __cplusplus +#include <vector> +#include <algorithm> +#endif + +enum {INSERT, ERASE, FIND, ITER, DESTRUCT, N_TESTS}; +const char* operations[] = {"insert", "erase", "find", "iter", "destruct"}; +typedef struct { time_t t1, t2; uint64_t sum; float fac; } Range; +typedef struct { const char* name; Range test[N_TESTS]; } Sample; +enum {SAMPLES = 2, N = 150000000, S = 0x3ffc, R = 4}; +uint64_t seed = 1, mask1 = 0xfffffff, mask2 = 0xffff; + +static float secs(Range s) { return (float)(s.t2 - s.t1) / CLOCKS_PER_SEC; } + +#define i_val size_t +#define i_tag x +#include <stc/cvec.h> + +#ifdef __cplusplus +Sample test_std_vector() { + typedef std::vector<size_t> container; + Sample s = {"std,vector"}; + { + s.test[INSERT].t1 = clock(); + container con; + csrandom(seed); + c_forrange (N) con.push_back(crandom() & mask1); + s.test[INSERT].t2 = clock(); + s.test[INSERT].sum = con.size(); + s.test[ERASE].t1 = clock(); + c_forrange (N) con.pop_back(); + s.test[ERASE].t2 = clock(); + s.test[ERASE].sum = con.size(); + }{ + container con; + csrandom(seed); + c_forrange (N) con.push_back(crandom() & mask2); + s.test[FIND].t1 = clock(); + size_t sum = 0; + //container::iterator it; + // Iteration - not inherent find - skipping + //c_forrange (S) if ((it = std::find(con.begin(), con.end(), crandom() & mask2)) != con.end()) sum += *it; + s.test[FIND].t2 = clock(); + s.test[FIND].sum = sum; + s.test[ITER].t1 = clock(); + sum = 0; + c_forrange (R) c_forrange (i, N) sum += con[i]; + s.test[ITER].t2 = clock(); + s.test[ITER].sum = sum; + s.test[DESTRUCT].t1 = clock(); + } + s.test[DESTRUCT].t2 = clock(); + s.test[DESTRUCT].sum = 0; + return s; +} +#else +Sample test_std_vector() { Sample s = {"std-vector"}; return s;} +#endif + + + +Sample test_stc_vector() { + typedef cvec_x container; + Sample s = {"STC,vector"}; + { + s.test[INSERT].t1 = clock(); + container con = cvec_x_init(); + csrandom(seed); + c_forrange (N) cvec_x_push_back(&con, crandom() & mask1); + s.test[INSERT].t2 = clock(); + s.test[INSERT].sum = cvec_x_size(&con); + s.test[ERASE].t1 = clock(); + c_forrange (N) { cvec_x_pop_back(&con); } + s.test[ERASE].t2 = clock(); + s.test[ERASE].sum = cvec_x_size(&con); + cvec_x_drop(&con); + }{ + csrandom(seed); + container con = cvec_x_init(); + c_forrange (N) cvec_x_push_back(&con, crandom() & mask2); + s.test[FIND].t1 = clock(); + size_t sum = 0; + //cvec_x_iter it, end = cvec_x_end(&con); + //c_forrange (S) if ((it = cvec_x_find(&con, crandom() & mask2)).ref != end.ref) sum += *it.ref; + s.test[FIND].t2 = clock(); + s.test[FIND].sum = sum; + s.test[ITER].t1 = clock(); + sum = 0; + c_forrange (R) c_forrange (i, N) sum += con.data[i]; + s.test[ITER].t2 = clock(); + s.test[ITER].sum = sum; + s.test[DESTRUCT].t1 = clock(); + cvec_x_drop(&con); + } + s.test[DESTRUCT].t2 = clock(); + s.test[DESTRUCT].sum = 0; + return s; +} + +int main(int argc, char* argv[]) +{ + Sample std_s[SAMPLES + 1] = {{NULL}}, stc_s[SAMPLES + 1] = {{NULL}}; + c_forrange (i, SAMPLES) { + std_s[i] = test_std_vector(); + stc_s[i] = test_stc_vector(); + if (i > 0) c_forrange (j, N_TESTS) { + if (secs(std_s[i].test[j]) < secs(std_s[0].test[j])) std_s[0].test[j] = std_s[i].test[j]; + if (secs(stc_s[i].test[j]) < secs(stc_s[0].test[j])) stc_s[0].test[j] = stc_s[i].test[j]; + if (stc_s[i].test[j].sum != stc_s[0].test[j].sum) printf("Error in sum: test %lld, sample %lld\n", i, j); + } + } + const char* comp = argc > 1 ? argv[1] : "test"; + bool header = (argc > 2 && argv[2][0] == '1'); + float std_sum = 0, stc_sum = 0; + + c_forrange (j, N_TESTS) { + std_sum += secs(std_s[0].test[j]); + stc_sum += secs(stc_s[0].test[j]); + } + if (header) printf("Compiler,Library,C,Method,Seconds,Ratio\n"); + + c_forrange (j, N_TESTS) + printf("%s,%s n:%d,%s,%.3f,%.3f\n", comp, std_s[0].name, N, operations[j], secs(std_s[0].test[j]), 1.0f); + printf("%s,%s n:%d,%s,%.3f,%.3f\n", comp, std_s[0].name, N, "total", std_sum, 1.0f); + + c_forrange (j, N_TESTS) + printf("%s,%s n:%d,%s,%.3f,%.3f\n", comp, stc_s[0].name, N, operations[j], secs(stc_s[0].test[j]), secs(std_s[0].test[j]) ? secs(stc_s[0].test[j])/secs(std_s[0].test[j]) : 1.0f); + printf("%s,%s n:%d,%s,%.3f,%.3f\n", comp, stc_s[0].name, N, "total", stc_sum, stc_sum/std_sum); +} diff --git a/misc/benchmarks/plotbench/plot.py b/misc/benchmarks/plotbench/plot.py new file mode 100644 index 00000000..fa538285 --- /dev/null +++ b/misc/benchmarks/plotbench/plot.py @@ -0,0 +1,24 @@ +import sys +import seaborn as sns +import pandas as pd +import matplotlib.pyplot as plt +#sns.set_theme(style="whitegrid") + +comp = ['All compilers', 'Mingw-g++-10.30', 'Win-Clang-12', 'VC-19.28'] +n = int(sys.argv[1]) if len(sys.argv) > 1 else 0 +file = sys.argv[2] if len(sys.argv) > 2 else 'plot_win.csv' +df = pd.read_csv(file) +df = df[df.Method != 'total'] +if n > 0: + df = df[df.Compiler == comp[n]] + +g = sns.catplot(data=df, x='Method', y='Seconds', hue='Library', col='C', kind='bar', + ci=68, legend=False, col_wrap=2, sharex=False, aspect=1.4, height=3.1) +g.set_xlabels('') + +g.add_legend(bbox_to_anchor=(0.75, 0.2), borderaxespad=0.) + +g.fig.subplots_adjust(top=0.90, left=0.06, bottom=0.07) +g.fig.suptitle('Benchmark STC vs c++ std containers: %s' % comp[n], fontsize=15, y=0.98) + +plt.show() diff --git a/misc/benchmarks/plotbench/run_all.bat b/misc/benchmarks/plotbench/run_all.bat new file mode 100644 index 00000000..2edd0a1e --- /dev/null +++ b/misc/benchmarks/plotbench/run_all.bat @@ -0,0 +1,5 @@ +set out=plot_win.csv +echo Compiler,Library,C,Method,Seconds,Ratio> %out% +sh run_gcc.sh >> %out% +sh run_clang.sh >> %out% +call run_vc.bat >> %out% diff --git a/misc/benchmarks/plotbench/run_all.sh b/misc/benchmarks/plotbench/run_all.sh new file mode 100644 index 00000000..f15a5881 --- /dev/null +++ b/misc/benchmarks/plotbench/run_all.sh @@ -0,0 +1,4 @@ +out="plot_linux.csv" +echo "Compiler,Library,C,Method,Seconds,Ratio"> $out +sh run_gcc.sh >> $out +sh run_clang.sh >> $out diff --git a/misc/benchmarks/plotbench/run_clang.sh b/misc/benchmarks/plotbench/run_clang.sh new file mode 100644 index 00000000..ae19486e --- /dev/null +++ b/misc/benchmarks/plotbench/run_clang.sh @@ -0,0 +1,14 @@ +exe='' +if [ "$OS" = "Windows_NT" ] ; then exe=".exe" ; fi +clang++ -I../include -O3 -o cdeq_benchmark$exe cdeq_benchmark.cpp +clang++ -I../include -O3 -o clist_benchmark$exe clist_benchmark.cpp +clang++ -I../include -O3 -o cmap_benchmark$exe cmap_benchmark.cpp +clang++ -I../include -O3 -o csmap_benchmark$exe csmap_benchmark.cpp +clang++ -I../include -O3 -o cvec_benchmark$exe cvec_benchmark.cpp + +c='Win-Clang-12' +./cdeq_benchmark$exe $c +./clist_benchmark$exe $c +./cmap_benchmark$exe $c +./csmap_benchmark$exe $c +./cvec_benchmark$exe $c diff --git a/misc/benchmarks/plotbench/run_gcc.sh b/misc/benchmarks/plotbench/run_gcc.sh new file mode 100644 index 00000000..6a6472c0 --- /dev/null +++ b/misc/benchmarks/plotbench/run_gcc.sh @@ -0,0 +1,12 @@ +g++ -I../include -O3 -o cdeq_benchmark cdeq_benchmark.cpp +g++ -I../include -O3 -o clist_benchmark clist_benchmark.cpp +g++ -I../include -O3 -o cmap_benchmark cmap_benchmark.cpp +g++ -I../include -O3 -o csmap_benchmark csmap_benchmark.cpp +g++ -I../include -O3 -o cvec_benchmark cvec_benchmark.cpp + +c='Mingw-g++-10.30' +./cdeq_benchmark $c +./clist_benchmark $c +./cmap_benchmark $c +./csmap_benchmark $c +./cvec_benchmark $c
\ No newline at end of file diff --git a/misc/benchmarks/plotbench/run_vc.bat b/misc/benchmarks/plotbench/run_vc.bat new file mode 100644 index 00000000..3dca925b --- /dev/null +++ b/misc/benchmarks/plotbench/run_vc.bat @@ -0,0 +1,15 @@ +@echo off +if "%VSINSTALLDIR%"=="" call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat" >nul +cl.exe -nologo -EHsc -std:c++latest -I../include -O2 cdeq_benchmark.cpp >nul +cl.exe -nologo -EHsc -std:c++latest -I../include -O2 clist_benchmark.cpp >nul +cl.exe -nologo -EHsc -std:c++latest -I../include -O2 cmap_benchmark.cpp >nul +cl.exe -nologo -EHsc -std:c++latest -I../include -O2 csmap_benchmark.cpp >nul +cl.exe -nologo -EHsc -std:c++latest -I../include -O2 cvec_benchmark.cpp >nul +del *.obj >nul + +set c=VC-19.28 +cdeq_benchmark.exe %c% +clist_benchmark.exe %c% +cmap_benchmark.exe %c% +csmap_benchmark.exe %c% +cvec_benchmark.exe %c%
\ No newline at end of file diff --git a/misc/benchmarks/shootout_hashmaps.cpp b/misc/benchmarks/shootout_hashmaps.cpp new file mode 100644 index 00000000..78d7bce2 --- /dev/null +++ b/misc/benchmarks/shootout_hashmaps.cpp @@ -0,0 +1,349 @@ +#include <stdio.h> +#include <time.h> +#include <stc/crandom.h> + +#define MAX_LOAD_FACTOR 77 + +#ifdef __cplusplus +#include <limits> +#include <unordered_map> +#include "external/ankerl/robin_hood.h" +#include "external/ankerl/unordered_dense.h" +#include "external/skarupke/flat_hash_map.hpp" +#include "external/tsl/robin_map.h" +#include "external/emhash/hash_table7.hpp" +#ifdef HAVE_BOOST +#include <boost/unordered/unordered_flat_map.hpp> +#endif +//#include "external/skarupke/bytell_hash_map.hpp" +//#include "external/parallel_hashmap/phmap.h" +//#include "external/tsl/hopscotch_map.h" + +template<typename C> inline void std_destroy(C& c) { C().swap(c); } + +template <class K, class V> using robin_hood_flat_map = robin_hood::unordered_flat_map< + K, V, robin_hood::hash<K>, std::equal_to<K>, MAX_LOAD_FACTOR>; +#endif + +typedef int64_t IKey; +typedef int64_t IValue; + +// khash template expansion +#include "external/khash.h" +KHASH_MAP_INIT_INT64(ii, IValue) + +// cmap template expansion +#define i_key IKey +#define i_val IValue +#define i_size uint32_t // optional, enables 2x expand +#define i_tag ii +#define i_max_load_factor MAX_LOAD_FACTOR / 100.0f +#include <stc/cmap.h> + +#define SEED(s) rng = stc64_new(s) +#define RAND(N) (stc64_rand(&rng) & (((uint64_t)1 << N) - 1)) + +#define CMAP_SETUP(X, Key, Value) cmap_##X map = cmap_##X##_init() +#define CMAP_PUT(X, key, val) cmap_##X##_insert_or_assign(&map, key, val).ref->second +#define CMAP_EMPLACE(X, key, val) cmap_##X##_insert(&map, key, val).ref->second +#define CMAP_ERASE(X, key) cmap_##X##_erase(&map, key) +#define CMAP_FIND(X, key) cmap_##X##_contains(&map, key) +#define CMAP_FOR(X, i) c_foreach (i, cmap_##X, map) +#define CMAP_ITEM(X, i) i.ref->second +#define CMAP_SIZE(X) cmap_##X##_size(&map) +#define CMAP_BUCKETS(X) cmap_##X##_bucket_count(&map) +#define CMAP_CLEAR(X) cmap_##X##_clear(&map) +#define CMAP_DTOR(X) cmap_##X##_drop(&map) + +#define KMAP_SETUP(X, Key, Value) khash_t(X)* map = kh_init(X); khiter_t ki; int ret +#define KMAP_PUT(X, key, val) (ki = kh_put(X, map, key, &ret), \ + map->vals[ki] = val, map->vals[ki]) +#define KMAP_EMPLACE(X, key, val) (ki = kh_put(X, map, key, &ret), \ + (ret ? map->vals[ki] = val, 1 : 0), map->vals[ki]) +#define KMAP_ERASE(X, key) (ki = kh_get(X, map, key), \ + ki != kh_end(map) ? (kh_del(X, map, ki), 1) : 0) +#define KMAP_FOR(X, i) for (khint_t i = kh_begin(map); i != kh_end(map); ++i) \ + if (kh_exist(map, i)) +#define KMAP_ITEM(X, i) map->vals[i] +#define KMAP_FIND(X, key) (kh_get(X, map, key) != kh_end(map)) +#define KMAP_SIZE(X) kh_size(map) +#define KMAP_BUCKETS(X) kh_n_buckets(map) +#define KMAP_CLEAR(X) kh_clear(X, map) +#define KMAP_DTOR(X) kh_destroy(X, map) + +#define UMAP_SETUP(X, Key, Value) std::unordered_map<Key, Value> map; \ + map.max_load_factor(MAX_LOAD_FACTOR/100.0f) +#define UMAP_PUT(X, key, val) (map[key] = val) +#define UMAP_EMPLACE(X, key, val) map.emplace(key, val).first->second +#define UMAP_FIND(X, key) int(map.find(key) != map.end()) +#define UMAP_ERASE(X, key) map.erase(key) +#define UMAP_FOR(X, i) for (const auto& i: map) +#define UMAP_ITEM(X, i) i.second +#define UMAP_SIZE(X) map.size() +#define UMAP_BUCKETS(X) map.bucket_count() +#define UMAP_CLEAR(X) map.clear() +#define UMAP_DTOR(X) std_destroy(map) + +#define FMAP_SETUP(X, Key, Value) ska::flat_hash_map<Key, Value> map; \ + map.max_load_factor(MAX_LOAD_FACTOR/100.0f) +#define FMAP_PUT(X, key, val) UMAP_PUT(X, key, val) +#define FMAP_EMPLACE(X, key, val) UMAP_EMPLACE(X, key, val) +#define FMAP_FIND(X, key) UMAP_FIND(X, key) +#define FMAP_ERASE(X, key) UMAP_ERASE(X, key) +#define FMAP_FOR(X, i) UMAP_FOR(X, i) +#define FMAP_ITEM(X, i) UMAP_ITEM(X, i) +#define FMAP_SIZE(X) UMAP_SIZE(X) +#define FMAP_BUCKETS(X) UMAP_BUCKETS(X) +#define FMAP_CLEAR(X) UMAP_CLEAR(X) +#define FMAP_DTOR(X) UMAP_DTOR(X) + +#define BMAP_SETUP(X, Key, Value) boost::unordered_flat_map<Key, Value> map; \ + map.max_load_factor(MAX_LOAD_FACTOR/100.0f) +#define BMAP_PUT(X, key, val) UMAP_PUT(X, key, val) +#define BMAP_EMPLACE(X, key, val) UMAP_EMPLACE(X, key, val) +#define BMAP_FIND(X, key) UMAP_FIND(X, key) +#define BMAP_ERASE(X, key) UMAP_ERASE(X, key) +#define BMAP_FOR(X, i) UMAP_FOR(X, i) +#define BMAP_ITEM(X, i) UMAP_ITEM(X, i) +#define BMAP_SIZE(X) UMAP_SIZE(X) +#define BMAP_BUCKETS(X) UMAP_BUCKETS(X) +#define BMAP_CLEAR(X) UMAP_CLEAR(X) +#define BMAP_DTOR(X) UMAP_DTOR(X) + +#define HMAP_SETUP(X, Key, Value) tsl::hopscotch_map<Key, Value> map; \ + map.max_load_factor(MAX_LOAD_FACTOR/100.0f) +#define HMAP_PUT(X, key, val) UMAP_PUT(X, key, val) +#define HMAP_EMPLACE(X, key, val) map.emplace(key, val).first.value() +#define HMAP_FIND(X, key) UMAP_FIND(X, key) +#define HMAP_ERASE(X, key) UMAP_ERASE(X, key) +#define HMAP_FOR(X, i) UMAP_FOR(X, i) +#define HMAP_ITEM(X, i) UMAP_ITEM(X, i) +#define HMAP_SIZE(X) UMAP_SIZE(X) +#define HMAP_BUCKETS(X) UMAP_BUCKETS(X) +#define HMAP_CLEAR(X) UMAP_CLEAR(X) +#define HMAP_DTOR(X) UMAP_DTOR(X) + +#define TMAP_SETUP(X, Key, Value) tsl::robin_map<Key, Value> map; \ + map.max_load_factor(MAX_LOAD_FACTOR/100.0f) +#define TMAP_PUT(X, key, val) UMAP_PUT(X, key, val) +#define TMAP_EMPLACE(X, key, val) map.emplace(key, val).first.value() +#define TMAP_FIND(X, key) UMAP_FIND(X, key) +#define TMAP_ERASE(X, key) UMAP_ERASE(X, key) +#define TMAP_FOR(X, i) UMAP_FOR(X, i) +#define TMAP_ITEM(X, i) UMAP_ITEM(X, i) +#define TMAP_SIZE(X) UMAP_SIZE(X) +#define TMAP_BUCKETS(X) UMAP_BUCKETS(X) +#define TMAP_CLEAR(X) UMAP_CLEAR(X) +#define TMAP_DTOR(X) UMAP_DTOR(X) + +#define RMAP_SETUP(X, Key, Value) robin_hood_flat_map<Key, Value> map +#define RMAP_PUT(X, key, val) UMAP_PUT(X, key, val) +#define RMAP_EMPLACE(X, key, val) UMAP_EMPLACE(X, key, val) +#define RMAP_FIND(X, key) UMAP_FIND(X, key) +#define RMAP_ERASE(X, key) UMAP_ERASE(X, key) +#define RMAP_FOR(X, i) UMAP_FOR(X, i) +#define RMAP_ITEM(X, i) UMAP_ITEM(X, i) +#define RMAP_SIZE(X) UMAP_SIZE(X) +#define RMAP_BUCKETS(X) (map.mask() + 1) +#define RMAP_CLEAR(X) UMAP_CLEAR(X) +#define RMAP_DTOR(X) UMAP_DTOR(X) + +#define DMAP_SETUP(X, Key, Value) ankerl::unordered_dense::map<Key, Value> map; \ + map.max_load_factor(MAX_LOAD_FACTOR/100.0f) +#define DMAP_PUT(X, key, val) UMAP_PUT(X, key, val) +#define DMAP_EMPLACE(X, key, val) UMAP_EMPLACE(X, key, val) +#define DMAP_FIND(X, key) UMAP_FIND(X, key) +#define DMAP_ERASE(X, key) UMAP_ERASE(X, key) +#define DMAP_FOR(X, i) UMAP_FOR(X, i) +#define DMAP_ITEM(X, i) UMAP_ITEM(X, i) +#define DMAP_SIZE(X) UMAP_SIZE(X) +#define DMAP_BUCKETS(X) UMAP_BUCKETS(X) +#define DMAP_CLEAR(X) UMAP_CLEAR(X) +#define DMAP_DTOR(X) UMAP_DTOR(X) + +#define EMAP_SETUP(X, Key, Value) emhash7::HashMap<Key, Value> map; \ + map.max_load_factor(MAX_LOAD_FACTOR/100.0f) +#define EMAP_PUT(X, key, val) UMAP_PUT(X, key, val) +#define EMAP_EMPLACE(X, key, val) UMAP_EMPLACE(X, key, val) +#define EMAP_FIND(X, key) UMAP_FIND(X, key) +#define EMAP_ERASE(X, key) UMAP_ERASE(X, key) +#define EMAP_FOR(X, i) UMAP_FOR(X, i) +#define EMAP_ITEM(X, i) UMAP_ITEM(X, i) +#define EMAP_SIZE(X) UMAP_SIZE(X) +#define EMAP_BUCKETS(X) UMAP_BUCKETS(X) +#define EMAP_CLEAR(X) UMAP_CLEAR(X) +#define EMAP_DTOR(X) UMAP_DTOR(X) + +#define PMAP_SETUP(X, Key, Value) phmap::flat_hash_map<Key, Value> map; \ + map.max_load_factor(MAX_LOAD_FACTOR/100.0f) +#define PMAP_PUT(X, key, val) UMAP_PUT(X, key, val) +#define PMAP_EMPLACE(X, key, val) UMAP_EMPLACE(X, key, val) +#define PMAP_FIND(X, key) UMAP_FIND(X, key) +#define PMAP_ERASE(X, key) UMAP_ERASE(X, key) +#define PMAP_FOR(X, i) UMAP_FOR(X, i) +#define PMAP_ITEM(X, i) UMAP_ITEM(X, i) +#define PMAP_SIZE(X) UMAP_SIZE(X) +#define PMAP_BUCKETS(X) UMAP_BUCKETS(X) +#define PMAP_CLEAR(X) UMAP_CLEAR(X) +#define PMAP_DTOR(X) UMAP_DTOR(X) + + +#define MAP_TEST1(M, X, n) \ +{ /* Insert, update */ \ + M##_SETUP(X, IKey, IValue); \ + uint64_t sum = 0; \ + SEED(seed); \ + clock_t difference, before = clock(); \ + for (size_t i = 0; i < n; ++i) { \ + sum += M##_PUT(X, RAND(keybits), i); \ + } \ + difference = clock() - before; \ + printf(#M ": %5.03f s, size: %" c_ZU ", buckets: %8" c_ZU ", sum: %" c_ZU "\n", \ + (float) difference / CLOCKS_PER_SEC, (size_t) M##_SIZE(X), (size_t) M##_BUCKETS(X), sum); \ + M##_DTOR(X); \ +} + +#define MAP_TEST2(M, X, n) \ +{ /* Insert sequential keys, then erase them */ \ + M##_SETUP(X, IKey, IValue); \ + size_t erased = 0; \ + clock_t difference, before = clock(); \ + for (size_t i = 0; i < n; ++i) \ + M##_EMPLACE(X, i, i); \ + for (size_t i = 0; i < n; ++i) \ + erased += M##_ERASE(X, i); \ + difference = clock() - before; \ + printf(#M ": %5.03f s, size: %" c_ZU ", buckets: %8" c_ZU ", erased %" c_ZU "\n", \ + (float) difference / CLOCKS_PER_SEC, (size_t) M##_SIZE(X), (size_t) M##_BUCKETS(X), erased); \ + M##_DTOR(X); \ +} + +#define MAP_TEST3(M, X, n) \ +{ /* Erase elements */ \ + M##_SETUP(X, IKey, IValue); \ + size_t erased = 0, _n = (n)*2; \ + clock_t difference, before; \ + SEED(seed); \ + for (size_t i = 0; i < _n; ++i) \ + M##_EMPLACE(X, RAND(keybits), i); \ + SEED(seed); \ + before = clock(); \ + for (size_t i = 0; i < _n; ++i) \ + erased += M##_ERASE(X, RAND(keybits)); \ + difference = clock() - before; \ + printf(#M ": %5.03f s, size: %" c_ZU ", buckets: %8" c_ZU ", erased %" c_ZU "\n", \ + (float) difference / CLOCKS_PER_SEC, (size_t) M##_SIZE(X), (size_t) M##_BUCKETS(X), erased); \ + M##_DTOR(X); \ +} + +#define MAP_TEST4(M, X, n) \ +{ /* Iterate */ \ + M##_SETUP(X, IKey, IValue); \ + size_t sum = 0, m = 1ull << (keybits + 1), _n = n; \ + if (_n < m) m = _n; \ + SEED(seed); \ + for (size_t i = 0; i < m; ++i) \ + M##_EMPLACE(X, RAND(keybits), i); \ + size_t rep = 60000000ull/M##_SIZE(X); \ + clock_t difference, before = clock(); \ + for (size_t k=0; k < rep; k++) M##_FOR (X, it) \ + sum += M##_ITEM(X, it); \ + difference = clock() - before; \ + printf(#M ": %5.03f s, size: %" c_ZU ", buckets: %8" c_ZU ", repeats: %" c_ZU ", check: %" c_ZU "\n", \ + (float) difference / CLOCKS_PER_SEC, (size_t) M##_SIZE(X), (size_t) M##_BUCKETS(X), rep, sum & 0xffff); \ + M##_DTOR(X); \ +} + +#define MAP_TEST5(M, X, n) \ +{ /* Lookup */ \ + M##_SETUP(X, IKey, IValue); \ + size_t found = 0, m = 1ull << (keybits + 1), _n = n; \ + clock_t difference, before; \ + if (_n < m) m = _n; \ + SEED(seed); \ + for (size_t i = 0; i < m; ++i) \ + M##_EMPLACE(X, RAND(keybits), i); \ + before = clock(); \ + /* Lookup x random keys */ \ + size_t x = m * 8000000ull/M##_SIZE(X); \ + for (size_t i = 0; i < x; ++i) \ + found += M##_FIND(X, RAND(keybits)); \ + /* Lookup x existing keys by resetting seed */ \ + SEED(seed); \ + for (size_t i = 0; i < x; ++i) \ + found += M##_FIND(X, RAND(keybits)); \ + difference = clock() - before; \ + printf(#M ": %5.03f s, size: %" c_ZU ", lookups: %" c_ZU ", found: %" c_ZU "\n", \ + (float) difference / CLOCKS_PER_SEC, (size_t) M##_SIZE(X), x*2, found); \ + M##_DTOR(X); \ +} + + +#ifdef __cplusplus +#ifdef HAVE_BOOST +#define MAP_TEST_BOOST(n, X) MAP_TEST##n(BMAP, X, N##n) +#else +#define MAP_TEST_BOOST(n, X) +#endif +#define RUN_TEST(n) MAP_TEST##n(KMAP, ii, N##n) \ + MAP_TEST_BOOST(n, ii) \ + MAP_TEST##n(CMAP, ii, N##n) \ + MAP_TEST##n(FMAP, ii, N##n) \ + MAP_TEST##n(TMAP, ii, N##n) \ + MAP_TEST##n(RMAP, ii, N##n) \ + MAP_TEST##n(DMAP, ii, N##n) \ + MAP_TEST##n(EMAP, ii, N##n) \ + MAP_TEST##n(UMAP, ii, N##n) +#else +#define RUN_TEST(n) MAP_TEST##n(KMAP, ii, N##n) \ + MAP_TEST##n(CMAP, ii, N##n) +#endif + +enum { + DEFAULT_N_MILL = 10, + DEFAULT_KEYBITS = 22, +}; + +int main(int argc, char* argv[]) +{ + if (argc < 2) { + printf("Usage %s n-million [key-bits (default %d)]\n\n", argv[0], DEFAULT_KEYBITS); + return 0; + } + unsigned n_mill = argc >= 2 ? atoi(argv[1]) : DEFAULT_N_MILL; + unsigned keybits = argc >= 3 ? atoi(argv[2]) : DEFAULT_KEYBITS; + unsigned n = n_mill * 1000000; + unsigned N1 = n, N2 = n, N3 = n, N4 = n, N5 = n; + stc64_t rng; + size_t seed = time(NULL); + + printf("\nUnordered hash map shootout\n"); + printf("KMAP = https://github.com/attractivechaos/klib\n" + "BMAP = https://www.boost.org (unordered_flat_map)\n" + "CMAP = https://github.com/tylov/STC (**)\n" + //"PMAP = https://github.com/greg7mdp/parallel-hashmap\n" + "FMAP = https://github.com/skarupke/flat_hash_map\n" + "TMAP = https://github.com/Tessil/robin-map\n" + //"HMAP = https://github.com/Tessil/hopscotch-map\n" + "RMAP = https://github.com/martinus/robin-hood-hashing\n" + "DMAP = https://github.com/martinus/unordered_dense\n" + "EMAP = https://github.com//ktprime/emhash\n" + "UMAP = std::unordered_map\n\n"); + + printf("Seed = %" c_ZU ":\n", seed); + + printf("\nT1: Insert %g mill. random keys range [0, 2^%u): map[rnd] = i;\n", N1/1000000.0, keybits); + RUN_TEST(1) + + printf("\nT2: Insert %g mill. SEQUENTIAL keys, erase them in same order:\n", N2/1000000.0); + RUN_TEST(2) + + printf("\nT3: Erase all elements by lookup (%u mill. random inserts), key range [0, 2^%u)\n", n_mill*2, keybits); + RUN_TEST(3) + + printf("\nT4: Iterate map with Min(%u mill, 2^%u) inserts repeated times:\n", n_mill, keybits+1); + RUN_TEST(4) + + printf("\nT5: Lookup mix of random/existing keys in range [0, 2^%u). Num lookups depends on size.\n", keybits); + RUN_TEST(5) +} diff --git a/misc/examples/README.md b/misc/examples/README.md new file mode 100644 index 00000000..4c0aa763 --- /dev/null +++ b/misc/examples/README.md @@ -0,0 +1,4 @@ +Examples +======== +This folder contains various examples of STC container usage. + diff --git a/misc/examples/arc_containers.c b/misc/examples/arc_containers.c new file mode 100644 index 00000000..b577f2c8 --- /dev/null +++ b/misc/examples/arc_containers.c @@ -0,0 +1,79 @@ +// Create a stack and a list of shared pointers to maps, +// and demonstrate sharing and cloning of maps. +#define i_static +#include <stc/cstr.h> +#define i_type Map +#define i_key_str // strings +#define i_val int +#define i_keydrop(p) (printf("drop name: %s\n", cstr_str(p)), cstr_drop(p)) +#include <stc/csmap.h> + +#define i_type Arc // (atomic) ref. counted type +#define i_val Map +#define i_valdrop(p) (printf("drop Arc:\n"), Map_drop(p)) +// no need for atomic ref. count in single thread: +#define i_opt c_no_atomic|c_no_lookup +#include <stc/carc.h> + +#define i_type Stack +#define i_valboxed Arc // define i_valboxed for carc/cbox value (not i_val) +#define i_opt c_no_cmp +#include <stc/cvec.h> + +#define i_type List +#define i_valboxed Arc // as above +#define i_opt c_no_cmp +#include <stc/clist.h> + +int main() +{ + c_auto (Stack, stack) + c_auto (List, list) + { + // POPULATE stack with shared pointers to Maps: + Map *map; + map = Stack_push(&stack, Arc_from(Map_init()))->get; + Map_emplace(map, "Joey", 1990); + Map_emplace(map, "Mary", 1995); + Map_emplace(map, "Joanna", 1992); + + map = Stack_push(&stack, Arc_from(Map_init()))->get; + Map_emplace(map, "Rosanna", 2001); + Map_emplace(map, "Brad", 1999); + Map_emplace(map, "Jack", 1980); + + // POPULATE list: + map = List_push_back(&list, Arc_from(Map_init()))->get; + Map_emplace(map, "Steve", 1979); + Map_emplace(map, "Rick", 1974); + Map_emplace(map, "Tracy", 2003); + + // Share two Maps from the stack with the list using emplace (clone the carc): + List_push_back(&list, Arc_clone(stack.data[0])); + List_push_back(&list, Arc_clone(stack.data[1])); + + // Clone (deep copy) a Map from the stack to the list + // List will contain two shared and two unshared maps. + map = List_push_back(&list, Arc_from(Map_clone(*stack.data[1].get)))->get; + + // Add one more element to the cloned map: + Map_emplace_or_assign(map, "CLONED", 2021); + + // Add one more element to the shared map: + Map_emplace_or_assign(stack.data[1].get, "SHARED", 2021); + + + puts("STACKS"); + c_foreach (i, Stack, stack) { + c_forpair (name, year, Map, *i.ref->get) + printf(" %s:%d", cstr_str(_.name), *_.year); + puts(""); + } + puts("LIST"); + c_foreach (i, List, list) { + c_forpair (name, year, Map, *i.ref->get) + printf(" %s:%d", cstr_str(_.name), *_.year); + puts(""); + } + } +} diff --git a/misc/examples/arc_demo.c b/misc/examples/arc_demo.c new file mode 100644 index 00000000..087f90ac --- /dev/null +++ b/misc/examples/arc_demo.c @@ -0,0 +1,56 @@ +#include <stdio.h> +#include <string.h> + +void int_drop(int* x) { + printf("drop: %d\n", *x); +} + +// carc implements its own clone method using reference counting, +// so 'i_valclone' is not required to be defined (ignored). + +#define i_type Arc // set type name to be defined (instead of 'carc_int') +#define i_val int +#define i_valdrop int_drop // optional, just to display the elements destroyed +#include <stc/carc.h> // Arc + +#define i_keyboxed Arc // note: use i_keyboxed instead of i_key for carc/cbox elements +#include <stc/csset.h> // csset_Arc (like: std::set<std::shared_ptr<int>>) + +#define i_valboxed Arc // note: as above. +#include <stc/cvec.h> // cvec_Arc (like: std::vector<std::shared_ptr<int>>) + +int main() +{ + c_auto (cvec_Arc, vec) // declare and init vec, call cvec_Arc_drop() at scope exit + c_auto (csset_Arc, set) // declare and init set, call csset_Arc_drop() at scope exit + { + const int years[] = {2021, 2012, 2022, 2015}; + c_forrange (i, c_arraylen(years)) + cvec_Arc_push(&vec, Arc_from(years[i])); + + printf("vec:"); + c_foreach (i, cvec_Arc, vec) printf(" %d", *i.ref->get); + puts(""); + + // add odd numbers from vec to set + c_foreach (i, cvec_Arc, vec) + if (*i.ref->get & 1) + csset_Arc_insert(&set, Arc_clone(*i.ref)); // copy shared pointer => increments counter. + + // erase the two last elements in vec + cvec_Arc_pop_back(&vec); + cvec_Arc_pop_back(&vec); + + printf("vec:"); + c_foreach (i, cvec_Arc, vec) printf(" %d", *i.ref->get); + + printf("\nset:"); + c_foreach (i, csset_Arc, set) printf(" %d", *i.ref->get); + + c_with (Arc p = Arc_clone(vec.data[0]), Arc_drop(&p)) { + printf("\n%d is now owned by %ld objects\n", *p.get, *p.use_count); + } + + puts("\nDone"); + } +} diff --git a/misc/examples/arcvec_erase.c b/misc/examples/arcvec_erase.c new file mode 100644 index 00000000..c70d59d9 --- /dev/null +++ b/misc/examples/arcvec_erase.c @@ -0,0 +1,53 @@ +#include <stdio.h> + +void show_drop(int* x) { printf("drop: %d\n", *x); } + +#define i_type Arc +#define i_val int +#define i_valdrop show_drop +#define i_cmp c_default_cmp // enable object comparisons (default ptr compare) +#include <stc/carc.h> // Shared pointer to int + +#define i_type Vec +#define i_valboxed Arc +#include <stc/cvec.h> // Vec: cvec<Arc> + + +int main() +{ + c_auto (Vec, vec) + { + c_forlist (i, int, {2012, 1990, 2012, 2019, 2015}) + Vec_emplace(&vec, *i.ref); + + // clone the second 2012 and push it back. + // note: cloning make sure that vec.data[2] has ref count 2. + Vec_push(&vec, Arc_clone(vec.data[2])); + + printf("vec before erase :"); + c_foreach (i, Vec, vec) + printf(" %d", *i.ref->get); + + printf("\nerase vec.data[2]; or first matching value depending on compare.\n"); + Vec_iter it; + it = Vec_find(&vec, *vec.data[2].get); + if (it.ref != Vec_end(&vec).ref) + Vec_erase_at(&vec, it); + + int year = 2015; + it = Vec_find(&vec, year); // Ok as tmp only. + if (it.ref != Vec_end(&vec).ref) + Vec_erase_at(&vec, it); + + printf("vec after erase :"); + c_foreach (i, Vec, vec) + printf(" %d", *i.ref->get); + + Vec_sort(&vec); + printf("\nvec after sort :"); + c_foreach (i, Vec, vec) + printf(" %d", *i.ref->get); + + puts("\nDone"); + } +} diff --git a/misc/examples/astar.c b/misc/examples/astar.c new file mode 100644 index 00000000..d4a821f9 --- /dev/null +++ b/misc/examples/astar.c @@ -0,0 +1,166 @@ +// +// -- An A* pathfinder inspired by the excellent tutorial at Red Blob Games -- +// +// This is a reimplementation of the CTL example to STC: +// https://github.com/glouw/ctl/blob/master/examples/astar.c +// https://www.redblobgames.com/pathfinding/a-star/introduction.html +#include <stc/cstr.h> +#include <stdio.h> + +typedef struct +{ + int x; + int y; + int priorty; + int width; +} +point; + +point +point_init(int x, int y, int width) +{ + return (point) { x, y, 0, width }; +} + +int +point_cmp_priority(const point* a, const point* b) +{ + return c_default_cmp(&a->priorty, &b->priorty); +} + +int +point_equal(const point* a, const point* b) +{ + return a->x == b->x && a->y == b->y; +} + +point +point_from(const cstr* maze, const char* c, int width) +{ + int index = cstr_find(maze, c); + return point_init(index % width, index / width, width); +} + +int +point_index(const point* p) +{ + return p->x + p->width * p->y; +} + +int +point_key_cmp(const point* a, const point* b) +{ + int i = point_index(a); + int j = point_index(b); + return (i == j) ? 0 : (i < j) ? -1 : 1; +} + +#define i_val point +#define i_cmp point_cmp_priority +#include <stc/cpque.h> + +#define i_val point +#define i_opt c_no_cmp +#include <stc/cdeq.h> + +#define i_key point +#define i_val int +#define i_cmp point_key_cmp +#define i_tag pcost +#include <stc/csmap.h> + +#define i_key point +#define i_val point +#define i_cmp point_key_cmp +#define i_tag pstep +#include <stc/csmap.h> + +cdeq_point +astar(cstr* maze, int width) +{ + cdeq_point path = cdeq_point_init(); + + c_auto (cpque_point, front) + c_auto (csmap_pstep, from) + c_auto (csmap_pcost, costs) + { + point start = point_from(maze, "@", width); + point goal = point_from(maze, "!", width); + csmap_pcost_insert(&costs, start, 0); + cpque_point_push(&front, start); + while (!cpque_point_empty(&front)) + { + point current = *cpque_point_top(&front); + cpque_point_pop(&front); + if (point_equal(¤t, &goal)) + break; + point deltas[] = { + { -1, +1, 0, width }, { 0, +1, 0, width }, { 1, +1, 0, width }, + { -1, 0, 0, width }, /* ~ ~ ~ ~ ~ ~ ~ */ { 1, 0, 0, width }, + { -1, -1, 0, width }, { 0, -1, 0, width }, { 1, -1, 0, width }, + }; + for (size_t i = 0; i < c_arraylen(deltas); i++) + { + point delta = deltas[i]; + point next = point_init(current.x + delta.x, current.y + delta.y, width); + int new_cost = *csmap_pcost_at(&costs, current); + if (cstr_str(maze)[point_index(&next)] != '#') + { + const csmap_pcost_value *cost = csmap_pcost_get(&costs, next); + if (cost == NULL || new_cost < cost->second) + { + csmap_pcost_insert(&costs, next, new_cost); + next.priorty = new_cost + abs(goal.x - next.x) + abs(goal.y - next.y); + cpque_point_push(&front, next); + csmap_pstep_insert(&from, next, current); + } + } + } + } + point current = goal; + while (!point_equal(¤t, &start)) + { + cdeq_point_push_front(&path, current); + current = *csmap_pstep_at(&from, current); + } + cdeq_point_push_front(&path, start); + } + return path; +} + +int +main(void) +{ + c_with (cstr maze = cstr_lit( + "#########################################################################\n" + "# # # # # # #\n" + "# # ######### # ##### ######### ##### ##### ##### # ! #\n" + "# # # # # # # # # #\n" + "######### # ######### ######### ##### # # # ######### #\n" + "# # # # # # # # # # #\n" + "# # ############# # # ######### ##### # ######### # #\n" + "# # # # # # # # # #\n" + "# ############# ##### ##### # ##### ######### # ##### #\n" + "# # # # # # # # # #\n" + "# ##### ##### # ##### # ######### # # # #############\n" + "# # # # # # # # # # # #\n" + "############# # # # ######### # ##### # ##### ##### #\n" + "# # # # # # # # # #\n" + "# ##### # ######### ##### # ##### ##### ############# #\n" + "# # # # # # # # # #\n" + "# # ######### # ##### ######### # # ############# # #\n" + "# # # # # # # # # # #\n" + "# ######### # # # ##### ######### ######### # #########\n" + "# # # # # # # # # #\n" + "# @ # ##### ##### ##### ######### ##### # ######### # #\n" + "# # # # # # #\n" + "#########################################################################\n"), cstr_drop(&maze)) + { + int width = cstr_find(&maze, "\n") + 1; + c_with (cdeq_point path = astar(&maze, width), cdeq_point_drop(&path)) + { + c_foreach (it, cdeq_point, path) cstr_data(&maze)[point_index(it.ref)] = 'x'; + printf("%s", cstr_str(&maze)); + } + } +} diff --git a/misc/examples/birthday.c b/misc/examples/birthday.c new file mode 100644 index 00000000..48b8ceaa --- /dev/null +++ b/misc/examples/birthday.c @@ -0,0 +1,68 @@ +#include <math.h> +#include <stdio.h> +#include <time.h> +#include <stc/crandom.h> + +#define i_tag ic +#define i_key uint64_t +#define i_val uint8_t +#include <stc/cmap.h> + +static uint64_t seed = 12345; + +static void test_repeats(void) +{ + enum {BITS = 46, BITS_TEST = BITS/2 + 2}; + const static uint64_t N = 1ull << BITS_TEST; + const static uint64_t mask = (1ull << BITS) - 1; + + printf("birthday paradox: value range: 2^%d, testing repeats of 2^%d values\n", BITS, BITS_TEST); + stc64_t rng = stc64_new(seed); + c_auto (cmap_ic, m) + { + cmap_ic_reserve(&m, N); + c_forrange (i, N) { + uint64_t k = stc64_rand(&rng) & mask; + int v = cmap_ic_insert(&m, k, 0).ref->second += 1; + if (v > 1) printf("repeated value %" PRIu64 " (%d) at 2^%d\n", + k, v, (int) log2((double) i)); + } + } +} + +#define i_key uint32_t +#define i_val uint64_t +#define i_tag x +#include <stc/cmap.h> + +void test_distribution(void) +{ + enum {BITS = 26}; + printf("distribution test: 2^%d values\n", BITS); + stc64_t rng = stc64_new(seed); + const size_t N = 1ull << BITS ; + + c_auto (cmap_x, map) { + c_forrange (N) { + uint64_t k = stc64_rand(&rng); + cmap_x_insert(&map, k & 0xf, 0).ref->second += 1; + } + + uint64_t sum = 0; + c_foreach (i, cmap_x, map) sum += i.ref->second; + sum /= map.size; + + c_foreach (i, cmap_x, map) { + printf("%4" PRIu32 ": %" PRIu64 " - %" PRIu64 ": %11.8f\n", + i.ref->first, i.ref->second, sum, + (1 - (double)i.ref->second / sum)); + } + } +} + +int main() +{ + seed = time(NULL); + test_distribution(); + test_repeats(); +} diff --git a/misc/examples/bits.c b/misc/examples/bits.c new file mode 100644 index 00000000..82fd65ec --- /dev/null +++ b/misc/examples/bits.c @@ -0,0 +1,61 @@ +#include <stdio.h> +#include <stc/cbits.h> + +int main() +{ + c_with (cbits set = cbits_with_size(23, true), cbits_drop(&set)) { + printf("count %" c_ZU ", %" c_ZU "\n", cbits_count(&set), cbits_size(&set)); + cbits s1 = cbits_from("1110100110111"); + char buf[256]; + cbits_to_str(&s1, buf, 0, 255); + printf("buf: %s: %" c_ZU "\n", buf, cbits_count(&s1)); + cbits_drop(&s1); + + cbits_reset(&set, 9); + cbits_resize(&set, 43, false); + printf(" str: %s\n", cbits_to_str(&set, buf, 0, 255)); + + printf("%4" c_ZU ": ", cbits_size(&set)); + c_forrange (i, cbits_size(&set)) + printf("%d", cbits_test(&set, i)); + puts(""); + + cbits_set(&set, 28); + cbits_resize(&set, 77, true); + cbits_resize(&set, 93, false); + cbits_resize(&set, 102, true); + cbits_set_value(&set, 99, false); + printf("%4" c_ZU ": ", cbits_size(&set)); + c_forrange (i, cbits_size(&set)) + printf("%d", cbits_test(&set, i)); + + puts("\nIterate:"); + printf("%4" c_ZU ": ", cbits_size(&set)); + c_forrange (i, cbits_size(&set)) + printf("%d", cbits_test(&set, i)); + puts(""); + + c_with (cbits s2 = cbits_clone(set), cbits_drop(&s2)) { + cbits_flip_all(&s2); + cbits_set(&s2, 16); + cbits_set(&s2, 17); + cbits_set(&s2, 18); + printf(" new: "); + c_forrange (i, cbits_size(&s2)) + printf("%d", cbits_test(&s2, i)); + puts(""); + + printf(" xor: "); + cbits_xor(&set, &s2); + c_forrange (i, cbits_size(&set)) + printf("%d", cbits_test(&set, i)); + puts(""); + + cbits_set_all(&set, false); + printf("%4" c_ZU ": ", cbits_size(&set)); + c_forrange (i, cbits_size(&set)) + printf("%d", cbits_test(&set, i)); + puts(""); + } + } +} diff --git a/misc/examples/bits2.c b/misc/examples/bits2.c new file mode 100644 index 00000000..59e0b337 --- /dev/null +++ b/misc/examples/bits2.c @@ -0,0 +1,41 @@ +#include <stdio.h> +// Example of static sized (stack allocated) bitsets + +#define i_type Bits +#define i_capacity 80 // enable fixed bitset on the stack +#include <stc/cbits.h> + +int main() +{ + Bits s1 = Bits_from("1110100110111"); + + printf("size %" c_ZU "\n", Bits_size(&s1)); + char buf[256]; + Bits_to_str(&s1, buf, 0, 256); + printf("buf: %s: count=%" c_ZU "\n", buf, Bits_count(&s1)); + + Bits_reset(&s1, 8); + printf(" s1: %s\n", Bits_to_str(&s1, buf, 0, 256)); + + Bits s2 = Bits_clone(s1); + + Bits_flip_all(&s2); + Bits_reset(&s2, 66); + Bits_reset(&s2, 67); + printf(" s2: "); + c_forrange (i, Bits_size(&s2)) + printf("%d", Bits_test(&s2, i)); + puts(""); + + printf("xor: "); + Bits_xor(&s1, &s2); + c_forrange (i, Bits_size(&s1)) + printf("%d", Bits_test(&s1, i)); + puts(""); + + printf("all: "); + Bits_set_pattern(&s1, 0x3333333333333333); + c_forrange (i, Bits_size(&s1)) + printf("%d", Bits_test(&s1, i)); + puts(""); +} diff --git a/misc/examples/books.c b/misc/examples/books.c new file mode 100644 index 00000000..b6067d81 --- /dev/null +++ b/misc/examples/books.c @@ -0,0 +1,60 @@ +// https://doc.rust-lang.org/std/collections/struct.HashMap.html +#include <stc/cstr.h> +#define i_key_str +#define i_val_str +#include <stc/cmap.h> + +// Type inference lets us omit an explicit type signature (which +// would be `HashMap<String, String>` in this example). +int main() +{ + c_auto (cmap_str, book_reviews) + { + // Review some books. + cmap_str_emplace(&book_reviews, + "Adventures of Huckleberry Finn", + "My favorite book." + ); + cmap_str_emplace(&book_reviews, + "Grimms' Fairy Tales", + "Masterpiece." + ); + cmap_str_emplace(&book_reviews, + "Pride and Prejudice", + "Very enjoyable" + ); + cmap_str_insert(&book_reviews, + cstr_lit("The Adventures of Sherlock Holmes"), + cstr_lit("Eye lyked it alot.") + ); + + // Check for a specific one. + // When collections store owned values (String), they can still be + // queried using references (&str). + if (cmap_str_contains(&book_reviews, "Les Misérables")) { + printf("We've got %" c_ZU " reviews, but Les Misérables ain't one.", + cmap_str_size(&book_reviews)); + } + + // oops, this review has a lot of spelling mistakes, let's delete it. + cmap_str_erase(&book_reviews, "The Adventures of Sherlock Holmes"); + + // Look up the values associated with some keys. + const char* to_find[] = {"Pride and Prejudice", "Alice's Adventure in Wonderland"}; + c_forrange (i, c_arraylen(to_find)) { + const cmap_str_value* b = cmap_str_get(&book_reviews, to_find[i]); + if (b) + printf("%s: %s\n", cstr_str(&b->first), cstr_str(&b->second)); + else + printf("%s is unreviewed.\n", to_find[i]); + } + + // Look up the value for a key (will panic if the key is not found). + printf("Review for Jane: %s\n", cstr_str(cmap_str_at(&book_reviews, "Pride and Prejudice"))); + + // Iterate over everything. + c_forpair (book, review, cmap_str, book_reviews) { + printf("%s: \"%s\"\n", cstr_str(_.book), cstr_str(_.review)); + } + } +} diff --git a/misc/examples/box.c b/misc/examples/box.c new file mode 100644 index 00000000..da13501f --- /dev/null +++ b/misc/examples/box.c @@ -0,0 +1,68 @@ +/* cbox: heap allocated boxed type */ +#include <stc/cstr.h> + +typedef struct { cstr name, last; } Person; + +Person Person_make(const char* name, const char* last) { + return (Person){.name = cstr_from(name), .last = cstr_from(last)}; +} + +uint64_t Person_hash(const Person* a) { + return cstr_hash(&a->name) ^ cstr_hash(&a->last); +} + +int Person_cmp(const Person* a, const Person* b) { + int c = cstr_cmp(&a->name, &b->name); + return c ? c : cstr_cmp(&a->last, &b->last); +} + +Person Person_clone(Person p) { + p.name = cstr_clone(p.name); + p.last = cstr_clone(p.last); + return p; +} + +void Person_drop(Person* p) { + printf("drop: %s %s\n", cstr_str(&p->name), cstr_str(&p->last)); + c_drop(cstr, &p->name, &p->last); +} + +#define i_type PBox +#define i_valclass Person // "class" binds _cmp, _clone, _drop functions. +#include <stc/cbox.h> + +#define i_type Persons +#define i_valboxed PBox // "arcbox" informs that PBox is a smart pointer. +#include <stc/csset.h> + +int main() +{ + c_auto (Persons, vec) + c_auto (PBox, p, q) + { + p = PBox_from(Person_make("Laura", "Palmer")); + q = PBox_clone(p); + cstr_assign(&q.get->name, "Leland"); + + printf("orig: %s %s\n", cstr_str(&p.get->name), cstr_str(&p.get->last)); + printf("copy: %s %s\n", cstr_str(&q.get->name), cstr_str(&q.get->last)); + + Persons_emplace(&vec, Person_make("Dale", "Cooper")); + Persons_emplace(&vec, Person_make("Audrey", "Home")); + + // NB! Clone/share p and q in the Persons container. + Persons_push(&vec, PBox_clone(p)); + Persons_push(&vec, PBox_clone(q)); + + c_foreach (i, Persons, vec) + printf("%s %s\n", cstr_str(&i.ref->get->name), cstr_str(&i.ref->get->last)); + puts(""); + + // Look-up Audrey! Create a temporary Person for lookup. + c_with (Person a = Person_make("Audrey", "Home"), Person_drop(&a)) { + const PBox *v = Persons_get(&vec, a); // lookup + if (v) printf("found: %s %s\n", cstr_str(&v->get->name), cstr_str(&v->get->last)); + } + puts(""); + } +} diff --git a/misc/examples/box2.c b/misc/examples/box2.c new file mode 100644 index 00000000..b9628eeb --- /dev/null +++ b/misc/examples/box2.c @@ -0,0 +1,90 @@ +// https://doc.rust-lang.org/rust-by-example/std/box.html + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stc/ccommon.h> + +struct { + double x; + double y; +} typedef Point; + +// A Rectangle can be specified by where its top left and bottom right +// corners are in space +struct { + Point top_left; + Point bottom_right; +} typedef Rectangle; + +#define i_val Point +#define i_opt c_no_lookup +#include <stc/cbox.h> // cbox_Point + +#define i_val Rectangle +#define i_opt c_no_lookup +#include <stc/cbox.h> // cbox_Rectangle + +// Box in box: +#define i_valboxed cbox_Point // NB: use i_valboxed when value is a cbox or carc! + // it will auto define i_valdrop, i_valfrom, and i_cmp. +#define i_tag BoxPoint +#define i_opt c_no_lookup +#include <stc/cbox.h> // cbox_BoxPoint + +Point origin(void) { + return (Point){ .x=1.0, .y=2.0 }; +} + +cbox_Point boxed_origin(void) { + // Allocate this point on the heap, and return a pointer to it + return cbox_Point_make((Point){ .x=2.0, .y=3.0 }); +} + + +int main(void) { + // Stack allocated variables + Point point = origin(); + Rectangle rectangle = (Rectangle){ + .top_left = origin(), + .bottom_right = (Point){ .x=3.0, .y=-4.0 } + }; + + // Declare auto-deleted box objects + c_auto (cbox_Rectangle, boxed_rectangle) + c_auto (cbox_Point, boxed_point) + c_auto (cbox_BoxPoint, box_in_a_box, boxbox2) + { + // Heap allocated rectangle + boxed_rectangle = cbox_Rectangle_make((Rectangle){ + .top_left = origin(), + .bottom_right = (Point){ .x=3.0, .y=-4.0 } + }); + + // The output of functions can be boxed + boxed_point = cbox_Point_from(origin()); + + // Double indirection + box_in_a_box = cbox_BoxPoint_make(boxed_origin()); + boxbox2 = cbox_BoxPoint_from(point); // !! + printf("boxbox2: x=%f\n", boxbox2.get->get->x); + + printf("Point occupies %" c_ZU " bytes on the stack\n", + sizeof(point)); + printf("Rectangle occupies %" c_ZU " bytes on the stack\n", + sizeof(rectangle)); + + // box size == pointer size + printf("Boxed point occupies %" c_ZU " bytes on the stack\n", + sizeof(boxed_point)); + printf("Boxed rectangle occupies %" c_ZU " bytes on the stack\n", + sizeof(boxed_rectangle)); + printf("Boxed box occupies %" c_ZU " bytes on the stack\n", + sizeof(box_in_a_box)); + + // Copy the data contained in `boxed_point` into `unboxed_point` + Point unboxed_point = *boxed_point.get; + printf("Unboxed point occupies %" c_ZU " bytes on the stack\n", + sizeof(unboxed_point)); + } +} diff --git a/misc/examples/city.c b/misc/examples/city.c new file mode 100644 index 00000000..8557c6cf --- /dev/null +++ b/misc/examples/city.c @@ -0,0 +1,91 @@ +#include <stc/cstr.h> + +typedef struct { + cstr name; + cstr country; + float lat, lon; + int population; +} City; + +int City_cmp(const City* a, const City* b); +uint64_t City_hash(const City* a); +City City_clone(City c); +void City_drop(City* c); + +#define i_type CityArc +#define i_valclass City +#include <stc/cbox.h> +//#include <stc/carc.h> // try instead of cbox.h + +#define i_type Cities +#define i_keyboxed CityArc +#include <stc/cvec.h> + +#define i_type CityMap +#define i_key int +#define i_valboxed CityArc +#include <stc/csmap.h> + + +int City_cmp(const City* a, const City* b) { + int c = cstr_cmp(&a->name, &b->name); + return c ? c : cstr_cmp(&a->country, &b->country); +} + +uint64_t City_hash(const City* a) { + return cstr_hash(&a->name) ^ cstr_hash(&a->country); +} + +City City_clone(City c) { + c.name = cstr_clone(c.name); + c.country = cstr_clone(c.country); + return c; +} + +void City_drop(City* c) { + printf("drop %s\n", cstr_str(&c->name)); + c_drop(cstr, &c->name, &c->country); +} + + +int main(void) +{ + c_auto (Cities, cities, copy) + c_auto (CityMap, map) + { + c_forlist (i, City, { + {cstr_lit("New York"), cstr_lit("US"), 4.3f, 23.2f, 9000000}, + {cstr_lit("Paris"), cstr_lit("France"), 4.3f, 23.2f, 9000000}, + {cstr_lit("Berlin"), cstr_lit("Germany"), 4.3f, 23.2f, 9000000}, + {cstr_lit("London"), cstr_lit("UK"), 4.3f, 23.2f, 9000000}, + }) Cities_emplace(&cities, *i.ref); // NB. creates smart pointers! + + Cities_sort(&cities); + + printf("Vec:\n"); + c_foreach (c, Cities, cities) + printf("city:%s, %d, use:%ld\n", cstr_str(&c.ref->get->name), + c.ref->get->population, + CityArc_use_count(c.ref)); + puts(""); + copy = Cities_clone(cities); // share each element! + + int k = 0, id[] = {8, 4, 3, 9, 2, 5}; + c_foreach (i, Cities, cities) + CityMap_insert(&map, id[k++], CityArc_clone(*i.ref)); + + Cities_pop(&cities); + Cities_pop(&cities); + + printf("Vec:\n"); + c_foreach (c, Cities, cities) + printf("city:%s, %d, use:%ld\n", cstr_str(&c.ref->get->name), + c.ref->get->population, + CityArc_use_count(c.ref)); + printf("\nMap:\n"); + c_forpair (id, city, CityMap, map) + printf("id:%d, city:%s, %d, use:%ld\n", *_.id, cstr_str(&_.city->get->name), + _.city->get->population, CityArc_use_count(_.city)); + puts(""); + } +} diff --git a/misc/examples/complex.c b/misc/examples/complex.c new file mode 100644 index 00000000..dd2f951a --- /dev/null +++ b/misc/examples/complex.c @@ -0,0 +1,55 @@ + +// Define similar c++ data types: +// +// using FloatStack = std::stack<float>; +// using StackList = std::stack<FloatStack>; +// using ListMap = std::unordered_map<int, std::forward_list<StackList>>; +// using MapMap = std::unordered_map<std::string, ListMap>; + +#include <stc/cstr.h> + + +#define i_type FloatStack +#define i_val float +#include <stc/cstack.h> + +#define i_type StackList +#define i_valclass FloatStack // "class" picks up _clone, _drop +#define i_opt c_no_cmp // no FloatStack_cmp() +#include <stc/clist.h> + +#define i_type ListMap +#define i_key int +#define i_valclass StackList // "class" picks up _clone, _drop +#include <stc/cmap.h> + +#define i_type MapMap +#define i_key_str +#define i_valclass ListMap +#include <stc/cmap.h> + + +int main() +{ + c_auto (MapMap, mmap) + { + FloatStack stack = FloatStack_with_size(10, 0); + + // Put in some data in the structures + stack.data[3] = 3.1415927f; + printf("stack size: %" c_ZU "\n", FloatStack_size(&stack)); + + StackList list = StackList_init(); + StackList_push_back(&list, stack); + + ListMap lmap = ListMap_init(); + ListMap_insert(&lmap, 42, list); + MapMap_insert(&mmap, cstr_from("first"), lmap); + + // Access the data entry + const ListMap* lmap_p = MapMap_at(&mmap, "first"); + const StackList* list_p = ListMap_at(lmap_p, 42); + const FloatStack* stack_p = StackList_back(list_p); + printf("value is: %f\n", *FloatStack_at(stack_p, 3)); // pi + } +} diff --git a/misc/examples/convert.c b/misc/examples/convert.c new file mode 100644 index 00000000..18be3c81 --- /dev/null +++ b/misc/examples/convert.c @@ -0,0 +1,50 @@ +#include <stc/cstr.h> + +#define i_key_str +#define i_val_str +#include <stc/cmap.h> + +#define i_val_str +#include <stc/cvec.h> + +#define i_val_str +#define i_extern // define _clist_mergesort() once +#include <stc/clist.h> + +int main() +{ + c_auto (cmap_str, map, mclone) + c_auto (cvec_str, keys, values) + c_auto (clist_str, list) + { + c_forlist (i, cmap_str_raw, { + {"green", "#00ff00"}, + {"blue", "#0000ff"}, + {"yellow", "#ffff00"}, + }) cmap_str_emplace(&map, c_PAIR(i.ref)); + + puts("MAP:"); + c_foreach (i, cmap_str, map) + printf(" %s: %s\n", cstr_str(&i.ref->first), cstr_str(&i.ref->second)); + + puts("\nCLONE MAP:"); + mclone = cmap_str_clone(map); + c_foreach (i, cmap_str, mclone) + printf(" %s: %s\n", cstr_str(&i.ref->first), cstr_str(&i.ref->second)); + + puts("\nCOPY MAP TO VECS:"); + c_foreach (i, cmap_str, mclone) { + cvec_str_emplace_back(&keys, cstr_str(&i.ref->first)); + cvec_str_emplace_back(&values, cstr_str(&i.ref->second)); + } + c_forrange (i, cvec_str_size(&keys)) + printf(" %s: %s\n", cstr_str(keys.data + i), cstr_str(values.data + i)); + + puts("\nCOPY VEC TO LIST:"); + c_foreach (i, cvec_str, keys) + clist_str_emplace_back(&list, cstr_str(i.ref)); + + c_foreach (i, clist_str, list) + printf(" %s\n", cstr_str(i.ref)); + } +} diff --git a/misc/examples/cpque.c b/misc/examples/cpque.c new file mode 100644 index 00000000..7254b0f7 --- /dev/null +++ b/misc/examples/cpque.c @@ -0,0 +1,72 @@ +// Implements c++ example: https://en.cppreference.com/w/cpp/container/priority_queue +// Example of per-instance less-function on a single priority queue type +// +// Note: i_less_functor: available for cpque types +// i_cmp_functor: available for csmap and csset types +// i_hash_functor/i_eq_functor: available for cmap and cset types + +#include <stdio.h> +#include <stdbool.h> +#include <stc/forward.h> +#include <stc/views.h> +#include <stc/cstr.h> + +// predeclare +forward_cpque(ipque, int); + +struct { + ipque Q; + bool (*less)(const int*, const int*); +} typedef IPQueue; + +#define IPQueue_drop(q) ipque_drop(&(q)->Q) + +#define i_type ipque +#define i_val int +#define i_opt c_is_forward // needed to avoid re-type-define container type +#define i_less_functor(self, x, y) c_container_of(self, IPQueue, Q)->less(x, y) // <== This. +#include <stc/cpque.h> + +#define print(name, q, n) do { \ + printf("%s: \t", name); \ + c_forrange (i, n) printf("%d ", q[i]); \ + puts(""); \ +} while(0) + +void print_queue(const char* name, IPQueue q) { + // NB: make a clone because there is no way to traverse + // priority_queue's content without erasing the queue. + IPQueue copy = {ipque_clone(q.Q), q.less}; + + for (printf("%s: \t", name); !ipque_empty(©.Q); ipque_pop(©.Q)) + printf("%d ", *ipque_top(©.Q)); + puts(""); + ipque_drop(©.Q); +} + +static bool int_less(const int* x, const int* y) { return *x < *y; } +static bool int_greater(const int* x, const int* y) { return *x > *y; } +static bool int_lambda(const int* x, const int* y) { return (*x ^ 1) < (*y ^ 1); } + +int main() +{ + const int data[] = {1,8,5,6,3,4,0,9,7,2}, n = c_arraylen(data); + print("data", data, n); + + c_autodrop (IPQueue, q1, {ipque_init(), int_less}) // Max priority queue + c_autodrop (IPQueue, minq1, {ipque_init(), int_greater}) // Min priority queue + c_autodrop (IPQueue, q5, {ipque_init(), int_lambda}) // Using lambda to compare elements. + { + c_forrange (i, n) + ipque_push(&q1.Q, data[i]); + print_queue("q1", q1); + + c_forrange (i, n) + ipque_push(&minq1.Q, data[i]); + print_queue("minq1", minq1); + + c_forrange (i, n) + ipque_push(&q5.Q, data[i]); + print_queue("q5", q5); + } +} diff --git a/misc/examples/csmap_erase.c b/misc/examples/csmap_erase.c new file mode 100644 index 00000000..48d8ceef --- /dev/null +++ b/misc/examples/csmap_erase.c @@ -0,0 +1,84 @@ +// map_erase.c +// From C++ example: https://docs.microsoft.com/en-us/cpp/standard-library/map-class?view=msvc-160#example-16 +#include <stc/cstr.h> +#include <stdio.h> + +#define i_key int +#define i_val_str +#define i_type mymap +#include <stc/csmap.h> + +void printmap(mymap m) +{ + c_foreach (elem, mymap, m) + printf(" [%d, %s]", elem.ref->first, cstr_str(&elem.ref->second)); + printf("\nsize() == %" c_ZU "\n\n", mymap_size(&m)); +} + +int main() +{ + c_auto (mymap, m1) + { + // Fill in some data to test with, one at a time + mymap_insert(&m1, 1, cstr_lit("A")); + mymap_insert(&m1, 2, cstr_lit("B")); + mymap_insert(&m1, 3, cstr_lit("C")); + mymap_insert(&m1, 4, cstr_lit("D")); + mymap_insert(&m1, 5, cstr_lit("E")); + + puts("Starting data of map m1 is:"); + printmap(m1); + // The 1st member function removes an element at a given position + mymap_erase_at(&m1, mymap_advance(mymap_begin(&m1), 1)); + puts("After the 2nd element is deleted, the map m1 is:"); + printmap(m1); + } + + c_auto (mymap, m2) + { + // Fill in some data to test with, one at a time + c_forlist (i, mymap_raw, { + {10, "Bob"}, + {11, "Rob"}, + {12, "Robert"}, + {13, "Bert"}, + {14, "Bobby"}, + }) mymap_emplace(&m2, c_PAIR(i.ref)); + + puts("Starting data of map m2 is:"); + printmap(m2); + mymap_iter it1 = mymap_advance(mymap_begin(&m2), 1); + mymap_iter it2 = mymap_find(&m2, mymap_back(&m2)->first); + + puts("to remove:"); + c_foreach (i, mymap, it1, it2) + printf(" [%d, %s]", i.ref->first, cstr_str(&i.ref->second)); + puts(""); + // The 2nd member function removes elements + // in the range [First, Last) + mymap_erase_range(&m2, it1, it2); + puts("After the middle elements are deleted, the map m2 is:"); + printmap(m2); + } + + c_auto (mymap, m3) + { + // Fill in some data to test with, one at a time, using emplace + mymap_emplace(&m3, 1, "red"); + mymap_emplace(&m3, 2, "yellow"); + mymap_emplace(&m3, 3, "blue"); + mymap_emplace(&m3, 4, "green"); + mymap_emplace(&m3, 5, "orange"); + mymap_emplace(&m3, 6, "purple"); + mymap_emplace(&m3, 7, "pink"); + + puts("Starting data of map m3 is:"); + printmap(m3); + // The 3rd member function removes elements with a given Key + size_t count = mymap_erase(&m3, 2); + // The 3rd member function also returns the number of elements removed + printf("The number of elements removed from m3 is: %" c_ZU "\n", count); + puts("After the element with a key of 2 is deleted, the map m3 is:"); + printmap(m3); + } +} diff --git a/misc/examples/csmap_find.c b/misc/examples/csmap_find.c new file mode 100644 index 00000000..3c507476 --- /dev/null +++ b/misc/examples/csmap_find.c @@ -0,0 +1,74 @@ +// This implements the c++ std::map::find example at: +// https://docs.microsoft.com/en-us/cpp/standard-library/map-class?view=msvc-160#example-17 +#include <stc/cstr.h> + +#define i_key int +#define i_val_str +#define i_tag istr +#include <stc/csmap.h> + +#define i_val csmap_istr_raw +#define i_opt c_no_cmp +#define i_tag istr +#include <stc/cvec.h> + +void print_elem(csmap_istr_raw p) { + printf("(%d, %s) ", p.first, p.second); +} + +#define using_print_collection(CX) \ + void print_collection_##CX(const CX* t) { \ + printf("%" c_ZU " elements: ", CX##_size(t)); \ + \ + c_foreach (p, CX, *t) { \ + print_elem(CX##_value_toraw(p.ref)); \ + } \ + puts(""); \ + } + +using_print_collection(csmap_istr) +using_print_collection(cvec_istr) + +void findit(csmap_istr c, csmap_istr_key val) +{ + printf("Trying find() on value %d\n", val); + csmap_istr_iter result = csmap_istr_find(&c, val); // prefer contains() or get() + if (result.ref != csmap_istr_end(&c).ref) { + printf("Element found: "); print_elem(csmap_istr_value_toraw(result.ref)); puts(""); + } else { + puts("Element not found."); + } +} + +int main() +{ + c_auto (csmap_istr, m1) + c_auto (cvec_istr, v) + { + c_forlist (i, csmap_istr_raw, {{40, "Zr"}, {45, "Rh"}}) + csmap_istr_emplace(&m1, c_PAIR(i.ref)); + + puts("The starting map m1 is (key, value):"); + print_collection_csmap_istr(&m1); + + typedef cvec_istr_value pair; + cvec_istr_push(&v, (pair){43, "Tc"}); + cvec_istr_push(&v, (pair){41, "Nb"}); + cvec_istr_push(&v, (pair){46, "Pd"}); + cvec_istr_push(&v, (pair){42, "Mo"}); + cvec_istr_push(&v, (pair){44, "Ru"}); + cvec_istr_push(&v, (pair){44, "Ru"}); // attempt a duplicate + + puts("Inserting the following vector data into m1:"); + print_collection_cvec_istr(&v); + + c_foreach (i, cvec_istr, cvec_istr_begin(&v), cvec_istr_end(&v)) + csmap_istr_emplace(&m1, c_PAIR(i.ref)); + + puts("The modified map m1 is (key, value):"); + print_collection_csmap_istr(&m1); + puts(""); + findit(m1, 45); + findit(m1, 6); + } +} diff --git a/misc/examples/csmap_insert.c b/misc/examples/csmap_insert.c new file mode 100644 index 00000000..8f777fc6 --- /dev/null +++ b/misc/examples/csmap_insert.c @@ -0,0 +1,112 @@ +#include <stc/cstr.h> + +// This implements the std::map insert c++ example at: +// https://docs.microsoft.com/en-us/cpp/standard-library/map-class?view=msvc-160#example-19 + +#define i_key int +#define i_val int +#define i_tag ii // Map of int => int +#include <stc/csmap.h> + +#define i_key int +#define i_val_str +#define i_tag istr // Map of int => cstr +#include <stc/csmap.h> + +#define i_val csmap_ii_raw +#define i_opt c_no_cmp +#define i_tag ii +#include <stc/cvec.h> + +void print_ii(csmap_ii map) { + c_foreach (e, csmap_ii, map) + printf("(%d, %d) ", e.ref->first, e.ref->second); + puts(""); +} + +void print_istr(csmap_istr map) { + c_foreach (e, csmap_istr, map) + printf("(%d, %s) ", e.ref->first, cstr_str(&e.ref->second)); + puts(""); +} + +int main() +{ + // insert single values + c_auto (csmap_ii, m1) { + csmap_ii_insert(&m1, 1, 10); + csmap_ii_insert(&m1, 2, 20); + + puts("The original key and mapped values of m1 are:"); + print_ii(m1); + + // intentionally attempt a duplicate, single element + csmap_ii_result ret = csmap_ii_insert(&m1, 1, 111); + if (!ret.inserted) { + csmap_ii_value pr = *ret.ref; + puts("Insert failed, element with key value 1 already exists."); + printf(" The existing element is (%d, %d)\n", pr.first, pr.second); + } + else { + puts("The modified key and mapped values of m1 are:"); + print_ii(m1); + } + puts(""); + + csmap_ii_insert(&m1, 3, 30); + puts("The modified key and mapped values of m1 are:"); + print_ii(m1); + puts(""); + } + + // The templatized version inserting a jumbled range + c_auto (csmap_ii, m2) + c_auto (cvec_ii, v) { + typedef cvec_ii_value ipair; + cvec_ii_push(&v, (ipair){43, 294}); + cvec_ii_push(&v, (ipair){41, 262}); + cvec_ii_push(&v, (ipair){45, 330}); + cvec_ii_push(&v, (ipair){42, 277}); + cvec_ii_push(&v, (ipair){44, 311}); + + puts("Inserting the following vector data into m2:"); + c_foreach (e, cvec_ii, v) + printf("(%d, %d) ", e.ref->first, e.ref->second); + puts(""); + + c_foreach (e, cvec_ii, v) + csmap_ii_insert_or_assign(&m2, e.ref->first, e.ref->second); + + puts("The modified key and mapped values of m2 are:"); + c_foreach (e, csmap_ii, m2) + printf("(%d, %d) ", e.ref->first, e.ref->second); + puts("\n"); + } + + // The templatized versions move-constructing elements + c_auto (csmap_istr, m3) { + csmap_istr_value ip1 = {475, cstr_lit("blue")}, ip2 = {510, cstr_lit("green")}; + + // single element + csmap_istr_insert(&m3, ip1.first, cstr_move(&ip1.second)); + puts("After the first move insertion, m3 contains:"); + print_istr(m3); + + // single element + csmap_istr_insert(&m3, ip2.first, cstr_move(&ip2.second)); + puts("After the second move insertion, m3 contains:"); + print_istr(m3); + puts(""); + } + + c_auto (csmap_ii, m4) { + // Insert the elements from an initializer_list + c_forlist (i, csmap_ii_raw, {{ 4, 44 }, { 2, 22 }, { 3, 33 }, + { 1, 11 }, { 5, 55 }}) + csmap_ii_insert(&m4, c_PAIR(i.ref)); + + puts("After initializer_list insertion, m4 contains:"); + print_ii(m4); + puts(""); + } +} diff --git a/misc/examples/csset_erase.c b/misc/examples/csset_erase.c new file mode 100644 index 00000000..33eb2163 --- /dev/null +++ b/misc/examples/csset_erase.c @@ -0,0 +1,43 @@ +#include <stdio.h> + +#define i_key int +#include <stc/csset.h> + +int main() +{ + c_auto (csset_int, set) + { + c_forlist (i, int, {30, 20, 80, 40, 60, 90, 10, 70, 50}) + csset_int_insert(&set, *i.ref); + + c_foreach (k, csset_int, set) + printf(" %d", *k.ref); + puts(""); + + int val = 64; + csset_int_iter it; + printf("Show values >= %d:\n", val); + it = csset_int_lower_bound(&set, val); + + c_foreach (k, csset_int, it, csset_int_end(&set)) + printf(" %d", *k.ref); + puts(""); + + printf("Erase values >= %d:\n", val); + while (it.ref != csset_int_end(&set).ref) + it = csset_int_erase_at(&set, it); + + c_foreach (k, csset_int, set) + printf(" %d", *k.ref); + puts(""); + + val = 40; + printf("Erase values < %d:\n", val); + it = csset_int_lower_bound(&set, val); + csset_int_erase_range(&set, csset_int_begin(&set), it); + + c_foreach (k, csset_int, set) + printf(" %d", *k.ref); + puts(""); + } +} diff --git a/misc/examples/cstr_match.c b/misc/examples/cstr_match.c new file mode 100644 index 00000000..286ba505 --- /dev/null +++ b/misc/examples/cstr_match.c @@ -0,0 +1,23 @@ +#include <stc/cstr.h> +#include <stc/csview.h> +#include <stdio.h> + +int main() +{ + c_with (cstr ss = cstr_lit("The quick brown fox jumps over the lazy dog.JPG"), cstr_drop(&ss)) { + size_t pos = cstr_find_at(&ss, 0, "brown"); + printf("%" c_ZU " [%s]\n", pos, pos == c_NPOS ? "<NULL>" : cstr_str(&ss) + pos); + printf("equals: %d\n", cstr_equals(&ss, "The quick brown fox jumps over the lazy dog.JPG")); + printf("contains: %d\n", cstr_contains(&ss, "umps ove")); + printf("starts_with: %d\n", cstr_starts_with(&ss, "The quick brown")); + printf("ends_with: %d\n", cstr_ends_with(&ss, ".jpg")); + printf("ends_with: %d\n", cstr_ends_with(&ss, ".JPG")); + + cstr s1 = cstr_lit("hell😀 w😀rl🐨"); + csview ch1 = cstr_u8_chr(&s1, 7); + csview ch2 = cstr_u8_chr(&s1, 10); + printf("%s\nsize: %" c_ZU ", %" c_ZU "\n", cstr_str(&s1), cstr_u8_size(&s1), cstr_size(&s1)); + printf("ch1: %.*s\n", c_ARGSV(ch1)); + printf("ch2: %.*s\n", c_ARGSV(ch2)); + } +} diff --git a/misc/examples/demos.c b/misc/examples/demos.c new file mode 100644 index 00000000..fc3771cb --- /dev/null +++ b/misc/examples/demos.c @@ -0,0 +1,228 @@ +#include <stc/cstr.h> + +void stringdemo1() +{ + printf("\nSTRINGDEMO1\n"); + c_with (cstr cs = cstr_lit("one-nine-three-seven-five"), cstr_drop(&cs)) + { + printf("%s.\n", cstr_str(&cs)); + + cstr_insert(&cs, 3, "-two"); + printf("%s.\n", cstr_str(&cs)); + + cstr_erase(&cs, 7, 5); // -nine + printf("%s.\n", cstr_str(&cs)); + + cstr_replace(&cs, "seven", "four", 1); + printf("%s.\n", cstr_str(&cs)); + + cstr_take(&cs, cstr_from_fmt("%s *** %s", cstr_str(&cs), cstr_str(&cs))); + printf("%s.\n", cstr_str(&cs)); + + printf("find \"four\": %s\n", cstr_str(&cs) + cstr_find(&cs, "four")); + + // reassign: + cstr_assign(&cs, "one two three four five six seven"); + cstr_append(&cs, " eight"); + printf("append: %s\n", cstr_str(&cs)); + } +} + +#define i_val int64_t +#define i_tag ix +#include <stc/cvec.h> + +void vectordemo1() +{ + printf("\nVECTORDEMO1\n"); + c_with (cvec_ix bignums = cvec_ix_with_capacity(100), cvec_ix_drop(&bignums)) + { + cvec_ix_reserve(&bignums, 100); + for (size_t i = 10; i <= 100; i += 10) + cvec_ix_push(&bignums, i * i); + + printf("erase - %d: %" PRIu64 "\n", 3, bignums.data[3]); + cvec_ix_erase_n(&bignums, 3, 1); // erase index 3 + + cvec_ix_pop(&bignums); // erase the last + cvec_ix_erase_n(&bignums, 0, 1); // erase the first + + for (size_t i = 0; i < cvec_ix_size(&bignums); ++i) { + printf("%" c_ZU ": %" PRIu64 "\n", i, bignums.data[i]); + } + } +} + +#define i_val_str +#include <stc/cvec.h> + +void vectordemo2() +{ + printf("\nVECTORDEMO2\n"); + c_auto (cvec_str, names) { + cvec_str_emplace_back(&names, "Mary"); + cvec_str_emplace_back(&names, "Joe"); + cvec_str_emplace_back(&names, "Chris"); + cstr_assign(&names.data[1], "Jane"); // replace Joe + printf("names[1]: %s\n", cstr_str(&names.data[1])); + + cvec_str_sort(&names); // Sort the array + c_foreach (i, cvec_str, names) + printf("sorted: %s\n", cstr_str(i.ref)); + } +} + +#define i_val int +#define i_tag ix +#define i_extern // define _clist_mergesort() once +#include <stc/clist.h> + +void listdemo1() +{ + printf("\nLISTDEMO1\n"); + c_auto (clist_ix, nums, nums2) + { + for (int i = 0; i < 10; ++i) + clist_ix_push_back(&nums, i); + for (int i = 100; i < 110; ++i) + clist_ix_push_back(&nums2, i); + + /* splice nums2 to front of nums */ + clist_ix_splice(&nums, clist_ix_begin(&nums), &nums2); + c_foreach (i, clist_ix, nums) + printf("spliced: %d\n", *i.ref); + puts(""); + + *clist_ix_find(&nums, 104).ref += 50; + clist_ix_remove(&nums, 103); + clist_ix_iter it = clist_ix_begin(&nums); + clist_ix_erase_range(&nums, clist_ix_advance(it, 5), clist_ix_advance(it, 15)); + clist_ix_pop_front(&nums); + clist_ix_push_back(&nums, -99); + clist_ix_sort(&nums); + + c_foreach (i, clist_ix, nums) + printf("sorted: %d\n", *i.ref); + } +} + +#define i_key int +#define i_tag i +#include <stc/cset.h> + +void setdemo1() +{ + printf("\nSETDEMO1\n"); + cset_i nums = cset_i_init(); + cset_i_insert(&nums, 8); + cset_i_insert(&nums, 11); + + c_foreach (i, cset_i, nums) + printf("set: %d\n", *i.ref); + cset_i_drop(&nums); +} + +#define i_key int +#define i_val int +#define i_tag ii +#include <stc/cmap.h> + +void mapdemo1() +{ + printf("\nMAPDEMO1\n"); + cmap_ii nums = cmap_ii_init(); + cmap_ii_insert(&nums, 8, 64); + cmap_ii_insert(&nums, 11, 121); + printf("val 8: %d\n", *cmap_ii_at(&nums, 8)); + cmap_ii_drop(&nums); +} + +#define i_key_str +#define i_val int +#define i_tag si +#include <stc/cmap.h> + +void mapdemo2() +{ + printf("\nMAPDEMO2\n"); + c_auto (cmap_si, nums) + { + cmap_si_emplace_or_assign(&nums, "Hello", 64); + cmap_si_emplace_or_assign(&nums, "Groovy", 121); + cmap_si_emplace_or_assign(&nums, "Groovy", 200); // overwrite previous + + // iterate the map: + for (cmap_si_iter i = cmap_si_begin(&nums); i.ref != cmap_si_end(&nums).ref; cmap_si_next(&i)) + printf("long: %s: %d\n", cstr_str(&i.ref->first), i.ref->second); + + // or rather use the short form: + c_foreach (i, cmap_si, nums) + printf("short: %s: %d\n", cstr_str(&i.ref->first), i.ref->second); + } +} + +#define i_key_str +#define i_val_str +#include <stc/cmap.h> + +void mapdemo3() +{ + printf("\nMAPDEMO3\n"); + cmap_str table = cmap_str_init(); + cmap_str_emplace(&table, "Map", "test"); + cmap_str_emplace(&table, "Make", "my"); + cmap_str_emplace(&table, "Sunny", "day"); + cmap_str_iter it = cmap_str_find(&table, "Make"); + c_foreach (i, cmap_str, table) + printf("entry: %s: %s\n", cstr_str(&i.ref->first), cstr_str(&i.ref->second)); + printf("size %" c_ZU ": remove: Make: %s\n", cmap_str_size(&table), cstr_str(&it.ref->second)); + //cmap_str_erase(&table, "Make"); + cmap_str_erase_at(&table, it); + + printf("size %" c_ZU "\n", cmap_str_size(&table)); + c_foreach (i, cmap_str, table) + printf("entry: %s: %s\n", cstr_str(&i.ref->first), cstr_str(&i.ref->second)); + cmap_str_drop(&table); // frees key and value cstrs, and hash table. +} + +#define i_val float +#define i_tag f +#include <stc/carr3.h> + +void arraydemo1() +{ + printf("\nARRAYDEMO1\n"); + c_with (carr3_f arr3 = carr3_f_with_size(30, 20, 10, 0.0f), + carr3_f_drop(&arr3)) + { + arr3.data[5][4][3] = 10.2f; + float **arr2 = arr3.data[5]; + float *arr1 = arr3.data[5][4]; + + printf("arr3: %" c_ZU ": (%" c_ZU ", %" c_ZU ", %" c_ZU ") = %" c_ZU "\n", sizeof(arr3), + arr3.xdim, arr3.ydim, arr3.zdim, carr3_f_size(&arr3)); + + printf("%g\n", arr1[3]); // = 10.2 + printf("%g\n", arr2[4][3]); // = 10.2 + printf("%g\n", arr3.data[5][4][3]); // = 10.2 + + float x = 0.0; + c_foreach (i, carr3_f, arr3) + *i.ref = ++x; + printf("%g\n", arr3.data[29][19][9]); // = 6000 + } +} + + +int main() +{ + stringdemo1(); + vectordemo1(); + vectordemo2(); + listdemo1(); + setdemo1(); + mapdemo1(); + mapdemo2(); + mapdemo3(); + arraydemo1(); +} diff --git a/misc/examples/forfilter.c b/misc/examples/forfilter.c new file mode 100644 index 00000000..ba4dce7f --- /dev/null +++ b/misc/examples/forfilter.c @@ -0,0 +1,144 @@ +#include <stdio.h> +#define i_extern +#include <stc/cstr.h> +#include <stc/csview.h> +#include <stc/views.h> + +#define i_type IVec +#define i_val int +#include <stc/cstack.h> + +#define i_type SVec +#define i_valclass csview +#include <stc/cstack.h> + +// filters and transforms: +#define flt_skipValue(i, x) (*i.ref != (x)) +#define flt_isEven(i) ((*i.ref & 1) == 0) +#define flt_isOdd(i) (*i.ref & 1) +#define flt_square(i) (*i.ref * *i.ref) + +void demo1(void) +{ + c_auto (IVec, vec) { + c_forlist (i, int, {0, 1, 2, 3, 4, 5, 80, 6, 7, 80, 8, 9, 80, 10, 11, 12, 13, 14, 15, 80, 16, 17}) + IVec_push(&vec, *i.ref); + + puts("demo1:"); + c_forfilter (i, IVec, vec, flt_skipValue(i, 80)) + printf(" %d", *i.ref); + puts(""); + + int res, sum = 0; + c_forfilter (i, IVec, vec + , c_flt_skipwhile(i, *i.ref != 80) + && c_flt_skip(i, 1) + && c_flt_skipwhile(i, *i.ref != 80) + && flt_isEven(i) + && flt_skipValue(i, 80) + , c_flt_take(i, 5) // short-circuit + ){ + sum += res = flt_square(i); + printf(" %d", res); + } + printf("\n sum: %d\n", sum); + } +} + + +/* Rust: +fn main() { + let vector = (1..) // Infinite range of integers + .skip_while(|x| *x != 11) // Skip initial numbers unequal 11 + .filter(|x| x % 2 != 0) // Collect odd numbers + .take(5) // Only take five numbers + .map(|x| x * x) // Square each number + .collect::<Vec<usize>>(); // Return as a new Vec<usize> + println!("{:?}", vector); // Print result +} +*/ + +void demo2(void) +{ + c_auto (IVec, vector) { + puts("demo2:"); + + c_forfilter (x, crange, crange_literal(INT64_MAX) + , c_flt_skipwhile(x, *x.ref != 11) + && *x.ref % 2 != 0 + , c_flt_take(x, 5)) + IVec_push(&vector, *x.ref * *x.ref); + c_foreach (x, IVec, vector) printf(" %d", *x.ref); + puts(""); + } +} + +/* Rust: +fn main() { + let sentence = "This is a sentence in Rust."; + let words: Vec<&str> = sentence + .split_whitespace() + .collect(); + let words_containing_i: Vec<&str> = words + .into_iter() + .filter(|word| word.contains("i")) + .collect(); + println!("{:?}", words_containing_i); +} +*/ +void demo3(void) +{ + c_auto (SVec, words, words_containing_i) { + const char* sentence = "This is a sentence in C99."; + c_fortoken (w, sentence, " ") + SVec_push(&words, *w.ref); + + c_forfilter (w, SVec, words, + csview_contains(*w.ref, "i")) + SVec_push(&words_containing_i, *w.ref); + + puts("demo3:"); + c_foreach (w, SVec, words_containing_i) + printf(" %.*s", c_ARGSV(*w.ref)); + puts(""); + } +} + +void demo4(void) +{ + csview s = c_SV("ab123cReAghNGnΩoEp"); // Ω = multi-byte + c_auto (cstr, out) { + c_forfilter (i, csview, s, utf8_isupper(utf8_peek(i.ref))) { + char chr[4]; + utf8_encode(chr, utf8_tolower(utf8_peek(i.ref))); + cstr_push(&out, chr); + } + puts("demo4:"); + printf(" %s\n", cstr_str(&out)); + } +} + +void demo5(void) +{ + #define flt_even(i) ((*i.ref & 1) == 0) + #define flt_mid_decade(i) ((*i.ref % 10) != 0) + puts("demo5:"); + crange r1 = crange_make(1963, INT32_MAX); + c_forfilter (i, crange, r1 + , c_flt_skip(i,15) + && c_flt_skipwhile(i, flt_mid_decade(i)) + && c_flt_skip(i,30) + && flt_even(i) + , c_flt_take(i,10)) + printf(" %lld", *i.ref); + puts(""); +} + +int main(void) +{ + demo1(); + demo2(); + demo3(); + demo4(); + demo5(); +} diff --git a/misc/examples/forloops.c b/misc/examples/forloops.c new file mode 100644 index 00000000..2654f095 --- /dev/null +++ b/misc/examples/forloops.c @@ -0,0 +1,81 @@ +#include <stdio.h>
+#include <stc/views.h>
+
+#define i_type IVec
+#define i_val int
+#include <stc/cstack.h>
+
+#define i_type IMap
+#define i_key int
+#define i_val int
+#include <stc/cmap.h>
+
+
+int main()
+{
+ puts("c_forrange:");
+ c_forrange (30) printf(" xx");
+ puts("");
+
+ c_forrange (i, 30) printf(" %lld", i);
+ puts("");
+
+ c_forrange (i, 30, 60) printf(" %lld", i);
+ puts("");
+
+ c_forrange (i, 30, 90, 2) printf(" %lld", i);
+
+
+ puts("\n\nc_forlist:");
+ c_forlist (i, int, {12, 23, 453, 65, 676})
+ printf(" %d", *i.ref);
+ puts("");
+
+ c_forlist (i, const char*, {"12", "23", "453", "65", "676"})
+ printf(" %s", *i.ref);
+ puts("");
+
+ c_forlist (i, const char*, {"12", "23", "453", "65", "676"})
+ printf(" %s", i.data[i.size - 1 - i.index]);
+
+
+ c_auto (IVec, vec)
+ c_auto (IMap, map)
+ {
+ c_forlist (i, int, {12, 23, 453, 65, 113, 215, 676, 34, 67, 20, 27, 66, 189, 45, 280, 199})
+ IVec_push(&vec, *i.ref);
+
+ c_forlist (i, IMap_value, {{12, 23}, {453, 65}, {676, 123}, {34, 67}})
+ IMap_push(&map, *i.ref);
+
+ puts("\n\nc_foreach:");
+ c_foreach (i, IVec, vec)
+ printf(" %d", *i.ref);
+ puts("");
+
+ c_foreach (i, IMap, map)
+ printf(" (%d %d)", i.ref->first, i.ref->second);
+
+ puts("\n\nc_forpair:");
+ c_forpair (key, val, IMap, map)
+ printf(" (%d %d)", *_.key, *_.val);
+
+ puts("\n\nc_forwhile:");
+ c_forwhile (i, IVec, IVec_begin(&vec), i.index < 3)
+ printf(" %d", *i.ref);
+
+ #define isOdd(i) (*i.ref & 1)
+
+ puts("\n\nc_forfilter:");
+ c_forfilter (i, IVec, vec
+ , c_flt_skipwhile(i, *i.ref != 65)
+ && c_flt_takewhile(i, *i.ref != 280)
+ && c_flt_skipwhile(i, isOdd(i))
+ && isOdd(i)
+ && c_flt_skip(i, 2)
+ , c_flt_take(i, 1))
+ printf(" %d", *i.ref);
+ puts("");
+ // 189
+ }
+}
diff --git a/misc/examples/gauss1.c b/misc/examples/gauss1.c new file mode 100644 index 00000000..675ff338 --- /dev/null +++ b/misc/examples/gauss1.c @@ -0,0 +1,56 @@ +#include <time.h> +#include <math.h> + +#include <stc/crandom.h> +#include <stc/cstr.h> + +// Declare int -> int hashmap. Uses typetag 'ii' for ints. +#define i_key int +#define i_val int +#define i_tag ii +#include <stc/cmap.h> + +// Declare int vector with entries from the cmap. +#define i_val cmap_ii_value +#define i_less(x, y) x->first < y->first +#define i_tag ii +#include <stc/cvec.h> + +int main() +{ + enum {N = 10000000}; + const double Mean = -12.0, StdDev = 6.0, Scale = 74; + + printf("Demo of gaussian / normal distribution of %d random samples\n", N); + + // Setup random engine with normal distribution. + uint64_t seed = time(NULL); + stc64_t rng = stc64_new(seed); + stc64_normalf_t dist = stc64_normalf_new(Mean, StdDev); + + // Create and init histogram vec and map with defered destructors: + c_auto (cvec_ii, histvec) + c_auto (cmap_ii, histmap) + { + c_forrange (N) { + int index = (int) round( stc64_normalf(&rng, &dist) ); + cmap_ii_insert(&histmap, index, 0).ref->second += 1; + } + + // Transfer map to vec and sort it by map keys. + c_foreach (i, cmap_ii, histmap) + cvec_ii_push(&histvec, (cmap_ii_value){i.ref->first, i.ref->second}); + + cvec_ii_sort(&histvec); + + // Print the gaussian bar chart + c_auto (cstr, bar) + c_foreach (i, cvec_ii, histvec) { + size_t n = (size_t) (i.ref->second * StdDev * Scale * 2.5 / (float)N); + if (n > 0) { + cstr_resize(&bar, n, '*'); + printf("%4d %s\n", i.ref->first, cstr_str(&bar)); + } + } + } +} diff --git a/misc/examples/gauss2.c b/misc/examples/gauss2.c new file mode 100644 index 00000000..2e07c5a5 --- /dev/null +++ b/misc/examples/gauss2.c @@ -0,0 +1,43 @@ +#include <stdio.h> +#include <time.h> + +#define STC_IMPLEMENT +#include <stc/crandom.h> +#include <stc/cstr.h> + +// Declare int -> int sorted map. +#define i_key int +#define i_val size_t +#include <stc/csmap.h> + +int main() +{ + enum {N = 10000000}; + const double Mean = -12.0, StdDev = 6.0, Scale = 74; + + printf("Demo of gaussian / normal distribution of %d random samples\n", N); + + // Setup random engine with normal distribution. + uint64_t seed = time(NULL); + stc64_t rng = stc64_new(seed); + stc64_normalf_t dist = stc64_normalf_new(Mean, StdDev); + + // Create and init histogram map with defered destruct + c_auto (csmap_int, mhist) + { + c_forrange (N) { + int index = (int) round( stc64_normalf(&rng, &dist) ); + csmap_int_insert(&mhist, index, 0).ref->second += 1; + } + + // Print the gaussian bar chart + c_auto (cstr, bar) + c_forpair (index, count, csmap_int, mhist) { + size_t n = (size_t) (*_.count * StdDev * Scale * 2.5 / (float)N); + if (n > 0) { + cstr_resize(&bar, n, '*'); + printf("%4d %s\n", *_.index, cstr_str(&bar)); + } + } + } +} diff --git a/misc/examples/hashmap.c b/misc/examples/hashmap.c new file mode 100644 index 00000000..f59ed824 --- /dev/null +++ b/misc/examples/hashmap.c @@ -0,0 +1,48 @@ +// https://doc.rust-lang.org/rust-by-example/std/hash.html +#include <stc/cstr.h> +#define i_key_str +#define i_val_str +#include <stdio.h> +#include <stc/cmap.h> + +const char* call(const char* number) { + if (!strcmp(number, "798-1364")) + return "We're sorry, the call cannot be completed as dialed." + " Please hang up and try again."; + else if (!strcmp(number, "645-7689")) + return "Hello, this is Mr. Awesome's Pizza. My name is Fred." + " What can I get for you today?"; + else + return "Hi! Who is this again?"; +} + +int main(void) { + c_auto (cmap_str, contacts) + { + cmap_str_emplace(&contacts, "Daniel", "798-1364"); + cmap_str_emplace(&contacts, "Ashley", "645-7689"); + cmap_str_emplace(&contacts, "Katie", "435-8291"); + cmap_str_emplace(&contacts, "Robert", "956-1745"); + + const cmap_str_value* v; + if ((v = cmap_str_get(&contacts, "Daniel"))) + printf("Calling Daniel: %s\n", call(cstr_str(&v->second))); + else + printf("Don't have Daniel's number."); + + cmap_str_emplace_or_assign(&contacts, "Daniel", "164-6743"); + + if ((v = cmap_str_get(&contacts, "Ashley"))) + printf("Calling Ashley: %s\n", call(cstr_str(&v->second))); + else + printf("Don't have Ashley's number."); + + cmap_str_erase(&contacts, "Ashley"); + + puts(""); + c_forpair (contact, number, cmap_str, contacts) { + printf("Calling %s: %s\n", cstr_str(_.contact), call(cstr_str(_.number))); + } + puts(""); + } +} diff --git a/misc/examples/inits.c b/misc/examples/inits.c new file mode 100644 index 00000000..021a3e0a --- /dev/null +++ b/misc/examples/inits.c @@ -0,0 +1,116 @@ +#include <stc/cstr.h> + +#define i_key int +#define i_val_str +#define i_tag id // Map of int => cstr +#include <stc/cmap.h> + +#define i_key_str +#define i_val int +#define i_tag cnt // Map of cstr => int +#include <stc/cmap.h> + +typedef struct {int x, y;} ipair_t; +inline static int ipair_cmp(const ipair_t* a, const ipair_t* b) { + int c = c_default_cmp(&a->x, &b->x); + return c ? c : c_default_cmp(&a->y, &b->y); +} + + +#define i_val ipair_t +#define i_cmp ipair_cmp +#define i_tag ip +#include <stc/cvec.h> + +#define i_val ipair_t +#define i_cmp ipair_cmp +#define i_tag ip +#define i_extern // define _clist_mergesort() once +#include <stc/clist.h> + +#define i_val float +#define i_tag f +#include <stc/cpque.h> + +int main(void) +{ + // CVEC FLOAT / PRIORITY QUEUE + + c_auto (cpque_f, floats) { + const float nums[] = {4.0f, 2.0f, 5.0f, 3.0f, 1.0f}; + + // PRIORITY QUEUE + c_forrange (i, c_arraylen(nums)) + cpque_f_push(&floats, nums[i]); + + puts("\npop and show high priorites first:"); + while (! cpque_f_empty(&floats)) { + printf("%.1f ", *cpque_f_top(&floats)); + cpque_f_pop(&floats); + } + puts("\n"); + } + + // CMAP ID + + int year = 2020; + c_auto (cmap_id, idnames) { + cmap_id_emplace(&idnames, 100, "Hello"); + cmap_id_insert(&idnames, 110, cstr_lit("World")); + cmap_id_insert(&idnames, 120, cstr_from_fmt("Howdy, -%d-", year)); + + c_foreach (i, cmap_id, idnames) + printf("%d: %s\n", i.ref->first, cstr_str(&i.ref->second)); + puts(""); + } + + // CMAP CNT + + c_auto (cmap_cnt, countries) { + c_forlist (i, cmap_cnt_raw, { + {"Norway", 100}, + {"Denmark", 50}, + {"Iceland", 10}, + {"Belgium", 10}, + {"Italy", 10}, + {"Germany", 10}, + {"Spain", 10}, + {"France", 10}, + }) cmap_cnt_emplace(&countries, c_PAIR(i.ref)); + + cmap_cnt_emplace(&countries, "Greenland", 0).ref->second += 20; + cmap_cnt_emplace(&countries, "Sweden", 0).ref->second += 20; + cmap_cnt_emplace(&countries, "Norway", 0).ref->second += 20; + cmap_cnt_emplace(&countries, "Finland", 0).ref->second += 20; + + c_forpair (country, health, cmap_cnt, countries) + printf("%s: %d\n", cstr_str(_.country), *_.health); + puts(""); + } + + // CVEC PAIR + + c_auto (cvec_ip, pairs1) { + c_forlist (i, ipair_t, {{5, 6}, {3, 4}, {1, 2}, {7, 8}}) + cvec_ip_push_back(&pairs1, *i.ref); + + cvec_ip_sort(&pairs1); + + c_foreach (i, cvec_ip, pairs1) + printf("(%d %d) ", i.ref->x, i.ref->y); + puts(""); + } + + // CLIST PAIR + + c_auto (clist_ip, pairs2) { + c_forlist (i, ipair_t, {{5, 6}, {3, 4}, {1, 2}, {7, 8}}) + clist_ip_push_back(&pairs2, *i.ref); + + clist_ip_sort(&pairs2); + + c_foreach (i, clist_ip, pairs2) + printf("(%d %d) ", i.ref->x, i.ref->y); + puts(""); + } +} diff --git a/misc/examples/intrusive.c b/misc/examples/intrusive.c new file mode 100644 index 00000000..acf4416d --- /dev/null +++ b/misc/examples/intrusive.c @@ -0,0 +1,52 @@ +// Example of intrusive list by using the node API and typesafe c_container_of(). + +#include <stdio.h> + +#define i_type List1 +#define i_val int +#define i_extern // implement List1_sort() +#include <stc/clist.h> + +#define i_type List2 +#define i_val List1_node +#define i_opt c_no_cmp // no elem. comparison +#include <stc/clist.h> + +int main() +{ + c_auto (List2, list2) + { + List1 list1 = List1_init(); // should not be destroyed, list2 will destroy shared nodes. + + c_forlist (i, int, {6, 9, 3, 1, 7, 4, 5, 2, 8}) + List2_push_back(&list2, (List1_node){NULL, *i.ref}); + + c_foreach (i, List2, list2) + List1_push_node_back(&list1, c_container_of(&i.ref->value, List1_node, value)); + + printf("list1:"); + c_foreach (i, List1, list1) printf(" %d", *i.ref); + printf("\nlist2:"); + c_foreach (i, List2, list2) printf(" %d", i.ref->value); + + printf("\nsort list1"); + List1_sort(&list1); + + printf("\nlist1:"); + c_foreach (i, List1, list1) printf(" %d", *i.ref); + printf("\nlist2:"); + c_foreach (i, List2, list2) printf(" %d", i.ref->value); + + printf("\nremove 5 from both lists in O(1) time"); + List1_iter it1 = List1_find(&list1, 5); + if (it1.ref) { + List1_unlink_node_after(&list1, it1.prev); + free(List2_unlink_node_after(&list2, c_container_of(it1.prev, List2_node, value))); + } + printf("\nlist1:"); + c_foreach (i, List1, list1) printf(" %d", *i.ref); + printf("\nlist2:"); + c_foreach (i, List2, list2) printf(" %d", i.ref->value); + puts(""); + } +} diff --git a/misc/examples/list.c b/misc/examples/list.c new file mode 100644 index 00000000..a538d93c --- /dev/null +++ b/misc/examples/list.c @@ -0,0 +1,61 @@ +#include <stdio.h> +#include <time.h> + +#define STC_IMPLEMENT +#define STC_EXTERN + +#define i_val double +#define i_tag fx +#include <stc/clist.h> +#include <stc/crandom.h> + +int main() { + const int n = 2000000; + + c_auto (clist_fx, list) + { + stc64_t rng = stc64_new(1234); + stc64_uniformf_t dist = stc64_uniformf_new(100.0f, n); + int m = 0; + c_forrange (n) + clist_fx_push_back(&list, stc64_uniformf(&rng, &dist)), ++m; + double sum = 0.0; + printf("sumarize %d:\n", m); + c_foreach (i, clist_fx, list) + sum += *i.ref; + printf("sum %f\n\n", sum); + + c_forwhile (i, clist_fx, clist_fx_begin(&list), i.index < 10) + printf("%8d: %10f\n", (int)i.index, *i.ref); + + puts("sort"); + clist_fx_sort(&list); // mergesort O(n*log n) + puts("sorted"); + + c_forwhile (i, clist_fx, clist_fx_begin(&list), i.index < 10) + printf("%8d: %10f\n", (int)i.index, *i.ref); + puts(""); + + clist_fx_clear(&list); + c_forlist (i, int, {10, 20, 30, 40, 30, 50}) + clist_fx_push_back(&list, *i.ref); + + const double* v = clist_fx_get(&list, 30); + printf("found: %f\n", *v); + c_foreach (i, clist_fx, list) printf(" %g", *i.ref); + puts(""); + + clist_fx_remove(&list, 30); + clist_fx_insert_at(&list, clist_fx_begin(&list), 5); // same as push_front() + clist_fx_push_back(&list, 500); + clist_fx_push_front(&list, 1964); + clist_fx_iter it = clist_fx_begin(&list); + printf("Full: "); + c_foreach (i, clist_fx, list) + printf(" %g", *i.ref); + printf("\nSubs: "); + c_foreach (i, clist_fx, clist_fx_advance(it, 4), clist_fx_end(&list)) + printf(" %g", *i.ref); + puts(""); + } +} diff --git a/misc/examples/list_erase.c b/misc/examples/list_erase.c new file mode 100644 index 00000000..c1a2aa97 --- /dev/null +++ b/misc/examples/list_erase.c @@ -0,0 +1,32 @@ +// erasing from clist +#include <stdio.h> + +#define i_type IList +#define i_val int +#include <stc/clist.h> + +int main () +{ + c_with (IList L = IList_init(), IList_drop(&L)) + { + c_forlist (i, int, {10, 20, 30, 40, 50}) + IList_push(&L, *i.ref); + + c_foreach (x, IList, L) + printf("%d ", *x.ref); + puts(""); + // 10 20 30 40 50 + IList_iter it = IList_begin(&L); // ^ + IList_next(&it); + it = IList_erase_at(&L, it); // 10 30 40 50 + // ^ + IList_iter end = IList_end(&L); // + IList_next(&it); + it = IList_erase_range(&L, it, end); // 10 30 + // ^ + printf("list contains:"); + c_foreach (x, IList, L) + printf(" %d", *x.ref); + puts(""); + } +} diff --git a/misc/examples/list_splice.c b/misc/examples/list_splice.c new file mode 100644 index 00000000..f7bac0d6 --- /dev/null +++ b/misc/examples/list_splice.c @@ -0,0 +1,43 @@ +#include <stdio.h> + +#define i_val int +#define i_tag i +#define i_extern // define _clist_mergesort() once +#include <stc/clist.h> + +void print_ilist(const char* s, clist_i list) +{ + printf("%s", s); + c_foreach (i, clist_i, list) { + printf(" %d", *i.ref); + } + puts(""); +} + +int main () +{ + c_auto (clist_i, list1, list2) + { + c_forlist (i, int, {1, 2, 3, 4, 5}) + clist_i_push_back(&list1, *i.ref); + + c_forlist (i, int, {10, 20, 30, 40, 50}) + clist_i_push_back(&list2, *i.ref); + + print_ilist("list1:", list1); + print_ilist("list2:", list2); + + clist_i_iter it = clist_i_advance(clist_i_begin(&list1), 2); + it = clist_i_splice(&list1, it, &list2); + + puts("After splice"); + print_ilist("list1:", list1); + print_ilist("list2:", list2); + + clist_i_splice_range(&list2, clist_i_begin(&list2), &list1, it, clist_i_end(&list1)); + + puts("After splice_range"); + print_ilist("list1:", list1); + print_ilist("list2:", list2); + } +} diff --git a/misc/examples/lower_bound.c b/misc/examples/lower_bound.c new file mode 100644 index 00000000..2477bc14 --- /dev/null +++ b/misc/examples/lower_bound.c @@ -0,0 +1,62 @@ +#include <stdio.h> + +#define i_val int +#include <stc/cvec.h> + +#define i_val int +#include <stc/csset.h> + +int main() +{ + // TEST SORTED VECTOR + c_auto (cvec_int, vec) + { + int key, *res; + + c_forlist (i, int, {40, 600, 1, 7000, 2, 500, 30}) + cvec_int_push(&vec, *i.ref); + + cvec_int_sort(&vec); + + key = 500; + res = cvec_int_lower_bound(&vec, key).ref; + if (res != cvec_int_end(&vec).ref) + printf("Sorted Vec %d: lower bound: %d\n", key, *res); // 600 + + key = 550; + res = cvec_int_lower_bound(&vec, key).ref; + if (res != cvec_int_end(&vec).ref) + printf("Sorted Vec %d: lower_bound: %d\n", key, *res); // 500 + + key = 500; + res = cvec_int_binary_search(&vec, key).ref; + if (res != cvec_int_end(&vec).ref) + printf("Sorted Vec %d: bin. search: %d\n", key, *res); // 500 + puts(""); + } + + // TEST SORTED SET + c_auto (csset_int, set) + { + int key, *res; + + c_forlist (i, int, {40, 600, 1, 7000, 2, 500, 30}) + csset_int_push(&set, *i.ref); + + key = 500; + res = csset_int_lower_bound(&set, key).ref; + if (res != csset_int_end(&set).ref) + printf("Sorted Set %d: lower bound: %d\n", key, *res); // 600 + + key = 550; + res = csset_int_lower_bound(&set, key).ref; + if (res != csset_int_end(&set).ref) + printf("Sorted Set %d: lower bound: %d\n", key, *res); // 600 + + key = 500; + res = csset_int_find(&set, key).ref; + if (res != csset_int_end(&set).ref) + printf("Sorted Set %d: find : %d\n", key, *res); // 600 + } + return 0; +} diff --git a/misc/examples/make.sh b/misc/examples/make.sh new file mode 100755 index 00000000..bd1392fc --- /dev/null +++ b/misc/examples/make.sh @@ -0,0 +1,56 @@ +#!/bin/sh + +if [ "$(uname)" = 'Linux' ]; then + sanitize='-fsanitize=address' + clibs='-lm' # -pthread + oflag='-o ' +fi + +#cc=gcc; cflags="-s -O2 -std=c99 -Werror -Wfatal-errors -Wpedantic -Wall $sanitize" +cc=gcc; cflags="-s -O2 -std=c99 -Werror -Wfatal-errors -Wpedantic -Wall -Wsign-compare -Wwrite-strings" # -Wconversion +#cc=tcc; cflags="-Wall -std=c99" +#cc=clang; cflags="-s -O2 -std=c99 -Werror -Wfatal-errors -Wpedantic -Wall -Wsign-compare -Wwrite-strings" +#cc=clang; cflags="-s -O2 -std=c99 -Werror -Wfatal-errors -Wpedantic -Wall -DSTC_CSTR_V1 -DSTC_CSMAP_V1" +#cc=gcc; cflags="-x c++ -s -O2 -Wall -std=c++20" +#cc=g++; cflags="-x c++ -s -O2 -Wall" +#cc=cl; cflags="-O2 -nologo -W2 -MD" +#cc=cl; cflags="-nologo -TP" +#cc=cl; cflags="-nologo -std:c11" + +if [ "$cc" = "cl" ]; then + oflag='/Fe:' +else + oflag='-o ' +fi + +run=0 +if [ "$1" = '-h' -o "$1" = '--help' ]; then + echo usage: runall.sh [-run] [compiler + options] + exit +fi +if [ "$1" = '-run' ]; then + run=1 + shift +fi +if [ ! -z "$1" ] ; then + comp="$@" +else + comp="$cc $cflags" +fi + +if [ $run = 0 ] ; then + for i in *.c ; do + echo $comp -I../include $i $clibs $oflag$(basename $i .c).exe + $comp -I../include $i $clibs $oflag$(basename $i .c).exe + done +else + for i in *.c ; do + echo $comp -I../include $i $clibs + $comp -I../include $i $clibs + if [ -f $(basename -s .c $i).exe ]; then ./$(basename -s .c $i).exe; fi + if [ -f ./a.exe ]; then ./a.exe; fi + if [ -f ./a.out ]; then ./a.out; fi + done +fi + +rm -f a.out *.o *.obj # *.exe diff --git a/misc/examples/mapmap.c b/misc/examples/mapmap.c new file mode 100644 index 00000000..488cc539 --- /dev/null +++ b/misc/examples/mapmap.c @@ -0,0 +1,68 @@ +// create a structure like: std::map<std::string, std::map<std::string, std::string>>: + +#include <stc/cstr.h> + +// People: std::map<std::string, std::string> +#define i_type People +#define i_key_str +#define i_val_str +#define i_keydrop(p) (printf("kdrop: %s\n", cstr_str(p)), cstr_drop(p)) // override +#include <stc/csmap.h> + +// Departments: std::map<std::string, People> +#define i_type Departments +#define i_key_str +#define i_valclass People +// Shorthand for: +// #define i_val People +// #define i_cmp People_cmp +// #define i_valclone People_clone +// #define i_valdrop People_drop +#include <stc/csmap.h> + + +void add(Departments* deps, const char* name, const char* email, const char* dep) +{ + People *people = &Departments_insert(deps, cstr_from(dep), People_init()).ref->second; + People_emplace_or_assign(people, name, email); +} + +int contains(Departments* map, const char* name) +{ + int count = 0; + c_foreach (i, Departments, *map) + if (People_contains(&i.ref->second, name)) + ++count; + return count; +} + +int main(void) +{ + c_auto (Departments, map) + { + add(&map, "Anna Kendro", "[email protected]", "Support"); + add(&map, "Terry Dane", "[email protected]", "Development"); + add(&map, "Kik Winston", "[email protected]", "Finance"); + add(&map, "Nancy Drew", "[email protected]", "Development"); + add(&map, "Nick Denton", "[email protected]", "Finance"); + add(&map, "Stan Whiteword", "[email protected]", "Marketing"); + add(&map, "Serena Bath", "[email protected]", "Support"); + add(&map, "Patrick Dust", "[email protected]", "Finance"); + add(&map, "Red Winger", "[email protected]", "Marketing"); + add(&map, "Nick Denton", "[email protected]", "Support"); + add(&map, "Colin Turth", "[email protected]", "Support"); + add(&map, "Dennis Kay", "[email protected]", "Marketing"); + add(&map, "Anne Dickens", "[email protected]", "Development"); + + c_foreach (i, Departments, map) + c_forpair (name, email, People, i.ref->second) + printf("%s: %s - %s\n", cstr_str(&i.ref->first), cstr_str(_.name), cstr_str(_.email)); + puts(""); + + printf("found Nick Denton: %d\n", contains(&map, "Nick Denton")); + printf("found Patrick Dust: %d\n", contains(&map, "Patrick Dust")); + printf("found Dennis Kay: %d\n", contains(&map, "Dennis Kay")); + printf("found Serena Bath: %d\n", contains(&map, "Serena Bath")); + puts("Done"); + } +} diff --git a/misc/examples/mmap.c b/misc/examples/mmap.c new file mode 100644 index 00000000..3934cf26 --- /dev/null +++ b/misc/examples/mmap.c @@ -0,0 +1,72 @@ +// This implements the multimap c++ example found at: +// https://en.cppreference.com/w/cpp/container/multimap/insert + +// Multimap entries +#include <stc/cstr.h> +#define i_val_str +//#define i_valdrop(x) (printf("drop %s\n", cstr_str(x)), cstr_drop(x)) +#define i_extern // define _clist_mergesort() once +#include <stc/clist.h> + +// Map of int => clist_str. +#define i_type Multimap +#define i_key int +#define i_valclass clist_str // uses clist_str as i_val and binds clist_str_clone, clist_str_drop +#define i_cmp -c_default_cmp // like std::greater<int> +#include <stc/csmap.h> + +void print(const char* lbl, const Multimap mmap) +{ + printf("%s ", lbl); + c_foreach (e, Multimap, mmap) { + c_foreach (s, clist_str, e.ref->second) + printf("{%d,%s} ", e.ref->first, cstr_str(s.ref)); + } + puts(""); +} + +void insert(Multimap* mmap, int key, const char* str) +{ + clist_str *list = &Multimap_insert(mmap, key, clist_str_init()).ref->second; + clist_str_emplace_back(list, str); +} + +int main() +{ + c_auto (Multimap, mmap) + { + typedef struct {int a; const char* b;} pair; + + // list-initialize + c_forlist (i, pair, {{2, "foo"}, {2, "bar"}, {3, "baz"}, {1, "abc"}, {5, "def"}}) + insert(&mmap, i.ref->a, i.ref->b); + print("#1", mmap); + + // insert using value_type + insert(&mmap, 5, "pqr"); + print("#2", mmap); + + // insert using make_pair + insert(&mmap, 6, "uvw"); + print("#3", mmap); + + insert(&mmap, 7, "xyz"); + print("#4", mmap); + + // insert using initialization_list + c_forlist (i, pair, {{5, "one"}, {5, "two"}}) + insert(&mmap, i.ref->a, i.ref->b); + print("#5", mmap); + + // FOLLOWING NOT IN ORIGINAL EXAMPLE: + // erase all entries with key 5 + Multimap_erase(&mmap, 5); + print("+5", mmap); + + + Multimap_clear(&mmap); + c_forlist (i, pair, {{1, "ä"}, {2, "ё"}, {2, "ö"}, {3, "ü"}}) + insert(&mmap, i.ref->a, i.ref->b); + print("#6", mmap); + } +} diff --git a/misc/examples/multimap.c b/misc/examples/multimap.c new file mode 100644 index 00000000..e72bdce3 --- /dev/null +++ b/misc/examples/multimap.c @@ -0,0 +1,99 @@ +#include <stc/cstr.h> + +// Olympics multimap example + +struct OlympicsData { int year; const char *city, *country, *date; } ol_data[] = { + {2026, "Milan and Cortina d'Ampezzo", "Italy", "February 6-22"}, + {2022, "Beijing", "China", "February 4-20"}, + {2018, "PyeongChang", "South Korea", "February 9-25"}, + {2014, "Sochi", "Russia", "February 7-23"}, + {2010, "Vancouver", "Canada", "February 12-28"}, + {2006, "Torino", "Italy", "February 10-26"}, + {2002, "Salt Lake City", "United States", "February 8-24"}, + {1998, "Nagano", "Japan", "February 7-22"}, + {1994, "Lillehammer", "Norway", "February 12-27"}, + {1992, "Albertville", "France", "February 8-23"}, + {1988, "Calgary", "Canada", "February 13-28"}, + {1984, "Sarajevo", "Yugoslavia", "February 8-19"}, + {1980, "Lake Placid", "United States", "February 13-24"}, + {1976, "Innsbruck", "Austria", "February 4-15"}, + {1972, "Sapporo", "Japan", "February 3-13"}, + {1968, "Grenoble", "France", "February 6-18"}, + {1964, "Innsbruck", "Austria", "January 29-February 9"}, + {1960, "Squaw Valley", "United States", "February 18-28"}, + {1956, "Cortina d'Ampezzo", "Italy", "January 26 - February 5"}, + {1952, "Oslo", "Norway", "February 14 - 25"}, + {1948, "St. Moritz", "Switzerland", "January 30 - February 8"}, + {1944, "canceled", "canceled", "canceled"}, + {1940, "canceled", "canceled", "canceled"}, + {1936, "Garmisch-Partenkirchen", "Germany", "February 6 - 16"}, + {1932, "Lake Placid", "United States", "February 4 - 15"}, + {1928, "St. Moritz", "Switzerland", "February 11 - 19"}, + {1924, "Chamonix", "France", "January 25 - February 5"}, +}; + +typedef struct { int year; cstr city, date; } OlympicLocation; + +int OlympicLocation_cmp(const OlympicLocation* a, const OlympicLocation* b); +OlympicLocation OlympicLocation_clone(OlympicLocation loc); +void OlympicLocation_drop(OlympicLocation* self); + +// Create a clist<OlympicLocation>, can be sorted by year. +#define i_valclass OlympicLocation // binds _cmp, _clone and _drop. +#define i_tag OL +#define i_extern // define _clist_mergesort() +#include <stc/clist.h> + +// Create a csmap<cstr, clist_OL> where key is country name +#define i_key_str // binds cstr_equ, cstr_hash, cstr_clone, ++ +#define i_valclass clist_OL // binds clist_OL_clone, clist_OL_drop +#define i_tag OL +#include <stc/csmap.h> + +int OlympicLocation_cmp(const OlympicLocation* a, const OlympicLocation* b) { + return a->year - b->year; +} + +OlympicLocation OlympicLocation_clone(OlympicLocation loc) { + loc.city = cstr_clone(loc.city); + loc.date = cstr_clone(loc.date); + return loc; +} +void OlympicLocation_drop(OlympicLocation* self) { + c_drop(cstr, &self->city, &self->date); +} + +int main() +{ + // Define the multimap with destructor defered to when block is completed. + c_auto (csmap_OL, multimap) + { + const clist_OL empty = clist_OL_init(); + + for (size_t i = 0; i < c_arraylen(ol_data); ++i) + { + struct OlympicsData* d = &ol_data[i]; + OlympicLocation loc = {.year = d->year, + .city = cstr_from(d->city), + .date = cstr_from(d->date)}; + // Insert an empty list for each new country, and append the entry to the list. + // If country already exist in map, its list is returned from the insert function. + clist_OL* list = &csmap_OL_insert(&multimap, cstr_from(d->country), empty).ref->second; + clist_OL_push_back(list, loc); + } + // Sort locations by year for each country. + c_foreach (country, csmap_OL, multimap) + clist_OL_sort(&country.ref->second); + + // Print the multimap: + c_foreach (country, csmap_OL, multimap) + { + // Loop the locations for a country sorted by year + c_foreach (loc, clist_OL, country.ref->second) + printf("%s: %d, %s, %s\n", cstr_str(&country.ref->first), + loc.ref->year, + cstr_str(&loc.ref->city), + cstr_str(&loc.ref->date)); + } + } +} diff --git a/misc/examples/music_arc.c b/misc/examples/music_arc.c new file mode 100644 index 00000000..162c4c2f --- /dev/null +++ b/misc/examples/music_arc.c @@ -0,0 +1,68 @@ +// shared_ptr-examples.cpp +// based on https://docs.microsoft.com/en-us/cpp/cpp/how-to-create-and-use-shared-ptr-instances?view=msvc-160 +#include <stc/cstr.h> + +struct Song +{ + cstr artist; + cstr title; +} typedef Song; + +int Song_cmp(const Song* x, const Song* y) + { return cstr_cmp(&x->title, &y->title); } + +Song Song_from(const char* artist, const char* title) + { return (Song){cstr_from(artist), cstr_from(title)}; } + +void Song_drop(Song* s) { + printf("drop: %s\n", cstr_str(&s->title)); + c_drop(cstr, &s->artist, &s->title); +} + +// Define the reference counted type +#define i_type SongArc +#define i_valclass Song +#define i_opt c_no_hash +#include <stc/carc.h> + +// ... and a vector of it +#define i_type SongVec +#define i_valboxed SongArc +#include <stc/cstack.h> + +void example3() +{ + c_auto (SongVec, vec1, vec2) + { + c_forlist (i, Song, { + Song_from("Bob Dylan", "The Times They Are A Changing"), + Song_from("Aretha Franklin", "Bridge Over Troubled Water"), + Song_from("Thalia", "Entre El Mar y Una Estrella") + }) SongVec_emplace(&vec1, *i.ref); + + // Share all entries in vec with vec2, except Bob Dylan. + c_foreach (s, SongVec, vec1) + if (!cstr_equals(&s.ref->get->artist, "Bob Dylan")) + SongVec_push(&vec2, SongArc_clone(*s.ref)); + + // Add a few more to vec2. We can use emplace when creating new entries + SongVec_emplace(&vec2, Song_from("Michael Jackson", "Billie Jean")); + SongVec_emplace(&vec2, Song_from("Rihanna", "Stay")); + // If we use push, we would need to construct the Arc explicitly (as in c++, make_shared): + // SongVec_push(&vec2, SongArc_from(Song_from("Rihanna", "Stay"))); + + // We now have two vectors with some shared, some unique entries. + c_forlist (i, SongVec, {vec1, vec2}) { + puts("VEC:"); + c_foreach (s, SongVec, *i.ref) + printf(" %s - %s, REFS: %ld\n", cstr_str(&s.ref->get->artist), + cstr_str(&s.ref->get->title), + *s.ref->use_count); + } + } // because the shared elem. are ref. counted, they are only dropped once here. +} + +int main() +{ + example3(); +} diff --git a/misc/examples/new_arr.c b/misc/examples/new_arr.c new file mode 100644 index 00000000..871cfae0 --- /dev/null +++ b/misc/examples/new_arr.c @@ -0,0 +1,57 @@ +#include <stc/cstr.h> + +#define i_val int +#include <stc/carr2.h> + +#define i_val int +#include <stc/carr3.h> + +#define i_val_str +#include <stc/carr2.h> + +int main() +{ + int w = 7, h = 5, d = 3; + + c_with (carr2_int volume = carr2_int_new_uninit(w, h), carr2_int_drop(&volume)) + { + int *dat = carr2_int_data(&volume); + for (size_t i = 0; i < carr2_int_size(&volume); ++i) + dat[i] = i; + + for (size_t x = 0; x < volume.xdim; ++x) + for (size_t y = 0; y < volume.ydim; ++y) + printf(" %d", volume.data[x][y]); + puts(""); + + c_foreach (i, carr2_int, volume) + printf(" %d", *i.ref); + puts("\n"); + } + + c_with (carr3_int volume = carr3_int_new_uninit(w, h, d), carr3_int_drop(&volume)) + { + int *dat = carr3_int_data(&volume); + for (size_t i = 0; i < carr3_int_size(&volume); ++i) + dat[i] = i; + + for (size_t x = 0; x < volume.xdim; ++x) + for (size_t y = 0; y < volume.ydim; ++y) + for (size_t z = 0; z < volume.zdim; ++z) + printf(" %d", volume.data[x][y][z]); + puts(""); + + c_foreach (i, carr3_int, volume) + printf(" %d", *i.ref); + puts(""); + } + + c_with (carr2_str text2d = carr2_str_with_size(h, d, cstr_NULL), carr2_str_drop(&text2d)) + { + cstr_assign(&text2d.data[2][1], "hello"); + cstr_assign(&text2d.data[4][0], "world"); + + c_foreach (i, carr2_str, text2d) + printf("line: %s\n", cstr_str(i.ref)); + } +} diff --git a/misc/examples/new_deq.c b/misc/examples/new_deq.c new file mode 100644 index 00000000..39149140 --- /dev/null +++ b/misc/examples/new_deq.c @@ -0,0 +1,61 @@ +#include <stc/cstr.h> +#include <stc/forward.h> + +forward_cdeq(cdeq_i32, int); +forward_cdeq(cdeq_pnt, struct Point); + +struct MyStruct { + cdeq_i32 intvec; + cdeq_pnt pntvec; +} typedef MyStruct; + + +#define i_val int +#define i_opt c_is_forward +#define i_tag i32 +#include <stc/cdeq.h> + +struct Point { int x, y; } typedef Point; +int point_cmp(const Point* a, const Point* b) { + int c = a->x - b->x; + return c ? c : a->y - b->y; +} + +#define i_val Point +#define i_cmp point_cmp +#define i_opt c_is_forward +#define i_tag pnt +#include <stc/cdeq.h> + +#define i_val float +#include <stc/cdeq.h> + +#define i_val_str +#include <stc/cdeq.h> + + +int main() +{ + c_auto (cdeq_i32, vec) + { + cdeq_i32_push_back(&vec, 123); + } + c_auto (cdeq_float, fvec) + { + cdeq_float_push_back(&fvec, 123.3f); + } + c_auto (cdeq_pnt, pvec) + { + cdeq_pnt_push_back(&pvec, (Point){42, 14}); + cdeq_pnt_push_back(&pvec, (Point){32, 94}); + cdeq_pnt_push_front(&pvec, (Point){62, 81}); + cdeq_pnt_sort(&pvec); + c_foreach (i, cdeq_pnt, pvec) + printf(" (%d %d)", i.ref->x, i.ref->y); + puts(""); + } + c_auto (cdeq_str, svec) + { + cdeq_str_emplace_back(&svec, "Hello, friend"); + } +} diff --git a/misc/examples/new_list.c b/misc/examples/new_list.c new file mode 100644 index 00000000..6dbe80b4 --- /dev/null +++ b/misc/examples/new_list.c @@ -0,0 +1,61 @@ +#include <stc/cstr.h> + +forward_clist(clist_i32, int); +forward_clist(clist_pnt, struct Point); + +struct MyStruct { + clist_i32 intlst; + clist_pnt pntlst; +} typedef MyStruct; + +#define i_val int +#define i_opt c_is_forward +#define i_tag i32 +#define i_extern // define _clist_mergesort() +#include <stc/clist.h> + +struct Point { int x, y; } typedef Point; +int point_cmp(const Point* a, const Point* b) { + int c = a->x - b->x; + return c ? c : a->y - b->y; +} + +#define i_val Point +#define i_cmp point_cmp +#define i_opt c_is_forward +#define i_tag pnt +#include <stc/clist.h> + +#define i_val float +#include <stc/clist.h> + +#define i_val_str +#include <stc/clist.h> + + +int main() +{ + c_auto (clist_i32, lst) + clist_i32_push_back(&lst, 123); + + c_auto (clist_pnt, plst) { + c_forlist (i, Point, {{42, 14}, {32, 94}, {62, 81}}) + clist_pnt_push_back(&plst, *i.ref); + + clist_pnt_sort(&plst); + + c_foreach (i, clist_pnt, plst) + printf(" (%d %d)", i.ref->x, i.ref->y); + puts(""); + } + + c_auto (clist_float, flst) { + c_forlist (i, float, {123.3f, 321.2f, -32.2f, 78.2f}) + clist_float_push_back(&flst, *i.ref); + + c_foreach (i, clist_float, flst) printf(" %g", *i.ref); + } + + c_auto (clist_str, slst) + clist_str_emplace_back(&slst, "Hello, friend"); +} diff --git a/misc/examples/new_map.c b/misc/examples/new_map.c new file mode 100644 index 00000000..f43d4217 --- /dev/null +++ b/misc/examples/new_map.c @@ -0,0 +1,73 @@ +#include <stc/cstr.h> +#include <stc/forward.h> + +forward_cmap(cmap_pnt, struct Point, int); + +struct MyStruct { + cmap_pnt pntmap; + cstr name; +} typedef MyStruct; + +// int => int map +#define i_key int +#define i_val int +#include <stc/cmap.h> + +// Point => int map +struct Point { int x, y; } typedef Point; + +int point_cmp(const Point* a, const Point* b) { + int c = a->x - b->x; + return c ? c : a->y - b->y; +} + +// Point => int map +#define i_key Point +#define i_val int +#define i_cmp point_cmp +#define i_hash c_default_hash +#define i_opt c_is_forward +#define i_tag pnt +#include <stc/cmap.h> + +// cstr => cstr map +#define i_key_str +#define i_val_str +#include <stc/cmap.h> + +// string set +#define i_key_str +#include <stc/cset.h> + + +int main() +{ + c_auto (cmap_int, map) + c_auto (cmap_pnt, pmap) + c_auto (cmap_str, smap) + c_auto (cset_str, sset) + { + cmap_int_insert(&map, 123, 321); + + c_forlist (i, cmap_pnt_raw, {{{42, 14}, 1}, {{32, 94}, 2}, {{62, 81}, 3}}) + cmap_pnt_insert(&pmap, c_PAIR(i.ref)); + + c_foreach (i, cmap_pnt, pmap) + printf(" (%d, %d: %d)", i.ref->first.x, i.ref->first.y, i.ref->second); + puts(""); + + c_forlist (i, cmap_str_raw, { + {"Hello, friend", "long time no see"}, + {"So long, friend", "see you around"}, + }) cmap_str_emplace(&smap, c_PAIR(i.ref)); + + c_forlist (i, const char*, { + "Hello, friend", + "Nice to see you again", + "So long, friend", + }) cset_str_emplace(&sset, *i.ref); + + c_foreach (i, cset_str, sset) + printf(" %s\n", cstr_str(i.ref)); + } +} diff --git a/misc/examples/new_pque.c b/misc/examples/new_pque.c new file mode 100644 index 00000000..2bb1d729 --- /dev/null +++ b/misc/examples/new_pque.c @@ -0,0 +1,55 @@ +#include <stdio.h> + +#define i_val int +#include <stc/cstack.h> +#define i_val int +#include <stc/cpque.h> + +struct Point { int x, y; } typedef Point; + +int Point_cmp(const Point* a, const Point* b) { + int c = a->x - b->x; + return c ? c : a->y - b->y; +} + +#define i_val Point +#define i_cmp Point_cmp +#define i_tag pnt +#include <stc/cpque.h> + + +int main() +{ + c_auto (cstack_int, istk) + { + cstack_int_push(&istk, 123); + cstack_int_push(&istk, 321); + // print + c_foreach (i, cstack_int, istk) + printf(" %d", *i.ref); + puts(""); + } + c_auto (cpque_pnt, pque) + { + cpque_pnt_push(&pque, (Point){23, 80}); + cpque_pnt_push(&pque, (Point){12, 32}); + cpque_pnt_push(&pque, (Point){54, 74}); + cpque_pnt_push(&pque, (Point){12, 62}); + // print + while (!cpque_pnt_empty(&pque)) { + const cpque_pnt_value *v = cpque_pnt_top(&pque); + printf(" (%d,%d)", v->x, v->y); + cpque_pnt_pop(&pque); + } + puts(""); + } + c_auto (cpque_int, ique) + { + cpque_int_push(&ique, 123); + cpque_int_push(&ique, 321); + // print + for (size_t i=0; i<cpque_int_size(&ique); ++i) + printf(" %d", ique.data[i]); + puts(""); + } +} diff --git a/misc/examples/new_queue.c b/misc/examples/new_queue.c new file mode 100644 index 00000000..bc7a95c9 --- /dev/null +++ b/misc/examples/new_queue.c @@ -0,0 +1,45 @@ +#include <stc/crandom.h> +#include <stc/forward.h> +#include <stdio.h> +#include <time.h> + +forward_cqueue(cqueue_pnt, struct Point); + +struct Point { int x, y; } typedef Point; +int point_cmp(const Point* a, const Point* b) { + int c = c_default_cmp(&a->x, &b->x); + return c ? c : c_default_cmp(&a->y, &b->y); +} +#define i_val Point +#define i_cmp point_cmp +#define i_opt c_is_forward +#define i_tag pnt +#include <stc/cqueue.h> + +#define i_type IQ +#define i_val int +#include <stc/cqueue.h> + +int main() { + int n = 50000000; + stc64_t rng = stc64_new(time(NULL)); + stc64_uniform_t dist = stc64_uniform_new(0, n); + + c_auto (IQ, Q) + { + // Push eight million random numbers onto the queue. + c_forrange (n) + IQ_push(&Q, stc64_uniform(&rng, &dist)); + + // Push or pop on the queue 50 million times + printf("befor: size %" c_ZU ", capacity %" c_ZU "\n", IQ_size(&Q), IQ_capacity(&Q)); + c_forrange (n) { + int r = stc64_uniform(&rng, &dist); + if (r & 3) + IQ_push(&Q, r); + else + IQ_pop(&Q); + } + printf("after: size %" c_ZU ", capacity %" c_ZU "\n", IQ_size(&Q), IQ_capacity(&Q)); + } +} diff --git a/misc/examples/new_smap.c b/misc/examples/new_smap.c new file mode 100644 index 00000000..c77aa185 --- /dev/null +++ b/misc/examples/new_smap.c @@ -0,0 +1,77 @@ +#include <stc/cstr.h> +#include <stc/forward.h> + +forward_csmap(PMap, struct Point, int); + +// Use forward declared PMap in struct +struct MyStruct { + PMap pntmap; + cstr name; +} typedef MyStruct; + +// int => int map +#define i_key int +#define i_val int +#include <stc/csmap.h> + +// Point => int map +struct Point { int x, y; } typedef Point; +int point_cmp(const Point* a, const Point* b) { + int c = a->x - b->x; + return c ? c : a->y - b->y; +} + +#define i_type PMap +#define i_key Point +#define i_val int +#define i_cmp point_cmp +#define i_opt c_is_forward +#include <stc/csmap.h> + +// cstr => cstr map +#define i_type SMap +#define i_key_str +#define i_val_str +#include <stc/csmap.h> + +// cstr set +#define i_type SSet +#define i_key_str +#include <stc/csset.h> + + +int main() +{ + c_auto (csmap_int, imap) { + csmap_int_insert(&imap, 123, 321); + } + + c_auto (PMap, pmap) { + c_forlist (i, PMap_value, { + {{42, 14}, 1}, + {{32, 94}, 2}, + {{62, 81}, 3}, + }) PMap_insert(&pmap, c_PAIR(i.ref)); + + c_forpair (p, i, PMap, pmap) + printf(" (%d,%d: %d)", _.p->x, _.p->y, *_.i); + puts(""); + } + + c_auto (SMap, smap) { + c_forlist (i, SMap_raw, { + {"Hello, friend", "this is the mapped value"}, + {"The brown fox", "jumped"}, + {"This is the time", "for all good things"}, + }) SMap_emplace(&smap, c_PAIR(i.ref)); + + c_forpair (i, j, SMap, smap) + printf(" (%s: %s)\n", cstr_str(_.i), cstr_str(_.j)); + } + + c_auto (SSet, sset) { + SSet_emplace(&sset, "Hello, friend"); + SSet_emplace(&sset, "Goodbye, foe"); + printf("Found? %s\n", SSet_contains(&sset, "Hello, friend") ? "true" : "false"); + } +} diff --git a/misc/examples/new_sptr.c b/misc/examples/new_sptr.c new file mode 100644 index 00000000..2c6b28d6 --- /dev/null +++ b/misc/examples/new_sptr.c @@ -0,0 +1,72 @@ +#include <stc/cstr.h> + +typedef struct { cstr name, last; } Person; +Person Person_make(const char* name, const char* last); +Person Person_clone(Person p); +void Person_drop(Person* p); +int Person_cmp(const Person* a, const Person* b); +uint64_t Person_hash(const Person* p); + +#define i_type PersonArc +#define i_valclass Person // "class" ensure Person_drop will be called +#define i_cmp Person_cmp // enable carc object comparisons (not ptr to obj) +#define i_hash Person_hash // enable carc object hash (not ptr to obj) +#include <stc/carc.h> + +#define i_type IPtr +#define i_val int +#define i_valdrop(x) printf("drop: %d\n", *x) +#include <stc/carc.h> + +#define i_type IPStack +#define i_valboxed IPtr +#include <stc/cstack.h> + +#define i_type PASet +#define i_valboxed PersonArc +#include <stc/cset.h> + + +Person Person_make(const char* name, const char* last) { + return (Person){.name = cstr_from(name), .last = cstr_from(last)}; +} +int Person_cmp(const Person* a, const Person* b) { + return cstr_cmp(&a->name, &b->name); +} +uint64_t Person_hash(const Person* p) { + return cstr_hash(&p->name); +} +Person Person_clone(Person p) { + p.name = cstr_clone(p.name), p.last = cstr_clone(p.last); + return p; +} +void Person_drop(Person* p) { + printf("drop: %s %s\n", cstr_str(&p->name), cstr_str(&p->last)); + c_drop(cstr, &p->name, &p->last); +} + + +int main(void) { + c_auto (PersonArc, p, q, r, s) + { + puts("Ex1"); + p = PersonArc_from(Person_make("John", "Smiths")); + q = PersonArc_clone(p); // share + r = PersonArc_clone(p); + s = PersonArc_from(Person_clone(*p.get)); // deep copy + printf("%s %s: refs %ld\n", cstr_str(&p.get->name), cstr_str(&p.get->last), *p.use_count); + } + c_auto (IPStack, vec) + { + puts("Ex2"); + IPStack_push(&vec, IPtr_from(10)); + IPStack_push(&vec, IPtr_from(20)); + IPStack_emplace(&vec, 30); // same as IPStack_push(&vec, IPtr_from(30)); + IPStack_push(&vec, IPtr_clone(*IPStack_back(&vec))); + IPStack_push(&vec, IPtr_clone(*IPStack_front(&vec))); + + c_foreach (i, IPStack, vec) + printf(" (%d: refs %ld)", *i.ref->get, *i.ref->use_count); + puts(""); + } +} diff --git a/misc/examples/new_vec.c b/misc/examples/new_vec.c new file mode 100644 index 00000000..84e4c7b2 --- /dev/null +++ b/misc/examples/new_vec.c @@ -0,0 +1,58 @@ +#include <stc/cstr.h> +#include <stc/forward.h> + +forward_cvec(cvec_i32, int); +forward_cvec(cvec_pnt, struct Point); + +struct MyStruct { + cvec_i32 intvec; + cvec_pnt pntvec; +} typedef MyStruct; + +#define i_val int +#define i_opt c_is_forward +#define i_tag i32 +#include <stc/cvec.h> + +struct Point { int x, y; } typedef Point; +int point_cmp(const Point* a, const Point* b) { + int c = c_default_cmp(&a->x, &b->x); + return c ? c : c_default_cmp(&a->y, &b->y); +} + +#define i_val Point +//#define i_cmp point_cmp +#define i_less(a, b) a->x < b->x || (a->x == b->x && a->y < b->y) +#define i_opt c_is_forward +#define i_tag pnt +#include <stc/cvec.h> + +#define i_val float +#include <stc/cvec.h> + +#define i_val_str +#include <stc/cvec.h> + + +int main() +{ + c_auto (cvec_i32, vec) + c_auto (cvec_float, fvec) + c_auto (cvec_pnt, pvec) + c_auto (cvec_str, svec) + { + cvec_i32_push(&vec, 123); + cvec_float_push(&fvec, 123.3f); + + cvec_pnt_push(&pvec, (Point){42, 14}); + cvec_pnt_push(&pvec, (Point){32, 94}); + cvec_pnt_push(&pvec, (Point){62, 81}); + cvec_pnt_push(&pvec, (Point){32, 91}); + cvec_pnt_sort(&pvec); + c_foreach (i, cvec_pnt, pvec) + printf(" (%d %d)", i.ref->x, i.ref->y); + puts(""); + + cvec_str_emplace(&svec, "Hello, friend"); + } +} diff --git a/misc/examples/person_arc.c b/misc/examples/person_arc.c new file mode 100644 index 00000000..a7bf2a6f --- /dev/null +++ b/misc/examples/person_arc.c @@ -0,0 +1,74 @@ +/* cbox: heap allocated boxed type */ +#include <stc/cstr.h> + +typedef struct { cstr name, last; } Person; + +Person Person_make(const char* name, const char* last) { + return (Person){.name = cstr_from(name), .last = cstr_from(last)}; +} + +int Person_cmp(const Person* a, const Person* b) { + int c = cstr_cmp(&a->name, &b->name); + return c ? c : cstr_cmp(&a->last, &b->last); +} + +uint64_t Person_hash(const Person* a) { + return cstr_hash(&a->name) ^ cstr_hash(&a->last); +} + +Person Person_clone(Person p) { + p.name = cstr_clone(p.name); + p.last = cstr_clone(p.last); + return p; +} + +void Person_drop(Person* p) { + printf("drop: %s %s\n", cstr_str(&p->name), cstr_str(&p->last)); + c_drop(cstr, &p->name, &p->last); +} + +#define i_type PSPtr +#define i_valclass Person // ensure Person_drop +#define i_cmp Person_cmp // specify object cmp, instead of ptr cmp for arc. +#include <stc/carc.h> + +#define i_type Persons +#define i_valboxed PSPtr // binds PSPtr_cmp, PSPtr_drop... +#include <stc/cvec.h> + + +int main() +{ + c_auto (Persons, vec) + c_auto (PSPtr, p, q) + { + p = PSPtr_from(Person_make("Laura", "Palmer")); + + // We want a deep copy -- PSPtr_clone(p) only shares! + q = PSPtr_from(Person_clone(*p.get)); + cstr_assign(&q.get->name, "Leland"); + + printf("orig: %s %s\n", cstr_str(&p.get->name), cstr_str(&p.get->last)); + printf("copy: %s %s\n", cstr_str(&q.get->name), cstr_str(&q.get->last)); + + // Use Persons_emplace to implicitly call PSPtr_make on the argument. + // No need to do: Persons_push(&vec, PSPtr_make(Person_make("Audrey", "Home"))); + Persons_emplace(&vec, Person_make("Audrey", "Home")); + Persons_emplace(&vec, Person_make("Dale", "Cooper")); + + // Clone/share p and q to the vector + c_forlist (i, PSPtr, {p, q}) + Persons_push(&vec, PSPtr_clone(*i.ref)); + + c_foreach (i, Persons, vec) + printf("%s %s\n", cstr_str(&i.ref->get->name), cstr_str(&i.ref->get->last)); + puts(""); + + // Look-up Audrey! + c_with (Person a = Person_make("Audrey", "Home"), Person_drop(&a)) { + const PSPtr *v = Persons_get(&vec, a); + if (v) printf("found: %s %s\n", cstr_str(&v->get->name), cstr_str(&v->get->last)); + } + puts(""); + } +} diff --git a/misc/examples/phonebook.c b/misc/examples/phonebook.c new file mode 100644 index 00000000..be068409 --- /dev/null +++ b/misc/examples/phonebook.c @@ -0,0 +1,82 @@ +// The MIT License (MIT) +// Copyright (c) 2018 Maksim Andrianov +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +// Program to emulates the phone book. + +#include <stc/cstr.h> + +#define i_key_str +#define i_val_str +#include <stc/cmap.h> + +#define i_key_str +#include <stc/cset.h> + +void print_phone_book(cmap_str phone_book) +{ + c_foreach (i, cmap_str, phone_book) + printf("%s\t- %s\n", cstr_str(&i.ref->first), cstr_str(&i.ref->second)); +} + +int main(int argc, char **argv) +{ + c_auto (cset_str, names) { + c_forlist (i, const char*, {"Hello", "Cool", "True"}) + cset_str_emplace(&names, *i.ref); + + c_foreach (i, cset_str, names) + printf("%s ", cstr_str(i.ref)); + puts(""); + } + + c_auto (cmap_str, phone_book) { + c_forlist (i, cmap_str_raw, { + {"Lilia Friedman", "(892) 670-4739"}, + {"Tariq Beltran", "(489) 600-7575"}, + {"Laiba Juarez", "(303) 885-5692"}, + {"Elliott Mooney", "(945) 616-4482"}, + }) cmap_str_emplace(&phone_book, c_PAIR(i.ref)); + + printf("Phone book:\n"); + print_phone_book(phone_book); + + cmap_str_emplace(&phone_book, "Zak Byers", "(551) 396-1880"); + cmap_str_emplace(&phone_book, "Zak Byers", "(551) 396-1990"); + + printf("\nPhone book after adding Zak Byers:\n"); + print_phone_book(phone_book); + + if (cmap_str_contains(&phone_book, "Tariq Beltran")) + printf("\nTariq Beltran is in phone book\n"); + + cmap_str_erase(&phone_book, "Tariq Beltran"); + cmap_str_erase(&phone_book, "Elliott Mooney"); + + printf("\nPhone book after erasing Tariq and Elliott:\n"); + print_phone_book(phone_book); + + cmap_str_emplace_or_assign(&phone_book, "Zak Byers", "(555) 396-188"); + + printf("\nPhone book after update phone of Zak Byers:\n"); + print_phone_book(phone_book); + } + puts("done"); +} diff --git a/misc/examples/prime.c b/misc/examples/prime.c new file mode 100644 index 00000000..287fb69b --- /dev/null +++ b/misc/examples/prime.c @@ -0,0 +1,51 @@ +#include <stdio.h> +#include <math.h> +#include <time.h> +#include <stc/cbits.h> +#include <stc/views.h> + +cbits sieveOfEratosthenes(size_t n) +{ + cbits bits = cbits_with_size(n/2 + 1, true); + size_t q = (size_t) sqrt((double) n) + 1; + for (size_t i = 3; i < q; i += 2) { + size_t j = i; + for (; j < n; j += 2) { + if (cbits_test(&bits, j>>1)) { + i = j; + break; + } + } + for (size_t j = i*i; j < n; j += i*2) + cbits_reset(&bits, j>>1); + } + return bits; +} + +int main(void) +{ + size_t n = 1000000000; + printf("computing prime numbers up to %" c_ZU "\n", n); + + clock_t t1 = clock(); + c_with (cbits primes = sieveOfEratosthenes(n + 1), cbits_drop(&primes)) { + puts("done"); + size_t np = cbits_count(&primes); + clock_t t2 = clock(); + + printf("number of primes: %" c_ZU ", time: %f\n", np, (t2 - t1) / (float)CLOCKS_PER_SEC); + puts("Show all the primes in the range [2, 1000):"); + printf("2"); + c_forrange (i, 3, 1000, 2) + if (cbits_test(&primes, i>>1)) printf(" %lld", i); + puts(""); + + puts("Show the last 50 primes using a temporary crange generator:"); + c_forfilter (i, crange, crange_literal(n - 1, 0, -2) + , cbits_test(&primes, *i.ref>>1) + , c_flt_take(i, 50)) { + printf("%lld ", *i.ref); + if (i.count % 10 == 0) puts(""); + } + } +} diff --git a/misc/examples/priority.c b/misc/examples/priority.c new file mode 100644 index 00000000..f6e63205 --- /dev/null +++ b/misc/examples/priority.c @@ -0,0 +1,35 @@ + +#include <stdio.h> +#include <time.h> +#include <stc/crandom.h> + +#define i_val int64_t +#define i_cmp -c_default_cmp // min-heap (increasing values) +#define i_tag i +#include <stc/cpque.h> + +int main() { + size_t N = 10000000; + stc64_t rng = stc64_new(time(NULL)); + stc64_uniform_t dist = stc64_uniform_new(0, N * 10); + c_auto (cpque_i, heap) + { + // Push ten million random numbers to priority queue + printf("Push %" c_ZU " numbers\n", N); + c_forrange (N) + cpque_i_push(&heap, stc64_uniform(&rng, &dist)); + + // push some negative numbers too. + c_forlist (i, int, {-231, -32, -873, -4, -343}) + cpque_i_push(&heap, *i.ref); + + c_forrange (N) + cpque_i_push(&heap, stc64_uniform(&rng, &dist)); + + puts("Extract the hundred smallest."); + c_forrange (100) { + printf("%" PRId64 " ", *cpque_i_top(&heap)); + cpque_i_pop(&heap); + } + } +} diff --git a/misc/examples/queue.c b/misc/examples/queue.c new file mode 100644 index 00000000..f39c4b8b --- /dev/null +++ b/misc/examples/queue.c @@ -0,0 +1,31 @@ +#include <stc/crandom.h> +#include <stdio.h> + +#define i_val int +#define i_tag i +#include <stc/cqueue.h> + +int main() { + int n = 100000000; + stc64_uniform_t dist; + stc64_t rng = stc64_new(1234); + dist = stc64_uniform_new(0, n); + + c_auto (cqueue_i, queue) + { + // Push ten million random numbers onto the queue. + c_forrange (n) + cqueue_i_push(&queue, stc64_uniform(&rng, &dist)); + + // Push or pop on the queue ten million times + printf("%d\n", n); + c_forrange (n) { // forrange uses initial n only. + int r = stc64_uniform(&rng, &dist); + if (r & 1) + ++n, cqueue_i_push(&queue, r); + else + --n, cqueue_i_pop(&queue); + } + printf("%d, %" c_ZU "\n", n, cqueue_i_size(&queue)); + } +} diff --git a/misc/examples/random.c b/misc/examples/random.c new file mode 100644 index 00000000..fe64290d --- /dev/null +++ b/misc/examples/random.c @@ -0,0 +1,45 @@ +#include <stdio.h> +#include <time.h> +#include <stc/crandom.h> + +int main() +{ + const size_t N = 1000000000; + const uint64_t seed = time(NULL), range = 1000000; + stc64_t rng = stc64_new(seed); + + uint64_t sum; + clock_t diff, before; + + printf("Compare speed of full and unbiased ranged random numbers...\n"); + sum = 0; + before = clock(); + c_forrange (N) { + sum += (uint32_t)stc64_rand(&rng); + } + diff = clock() - before; + printf("full range\t\t: %f secs, %" c_ZU ", avg: %f\n", + (float)diff / CLOCKS_PER_SEC, N, (double)sum / N); + + stc64_uniform_t dist1 = stc64_uniform_new(0, range); + rng = stc64_new(seed); + sum = 0; + before = clock(); + c_forrange (N) { + sum += stc64_uniform(&rng, &dist1); // unbiased + } + diff = clock() - before; + printf("unbiased 0-%" PRIu64 "\t: %f secs, %" c_ZU ", avg: %f\n", + range, (float)diff/CLOCKS_PER_SEC, N, (double)sum / N); + + sum = 0; + rng = stc64_new(seed); + before = clock(); + c_forrange (N) { + sum += stc64_rand(&rng) % (range + 1); // biased + } + diff = clock() - before; + printf("biased 0-%" PRIu64 " \t: %f secs, %" c_ZU ", avg: %f\n", + range, (float)diff / CLOCKS_PER_SEC, N, (double)sum / N); + +} diff --git a/misc/examples/rawptr_elements.c b/misc/examples/rawptr_elements.c new file mode 100644 index 00000000..4b3d2056 --- /dev/null +++ b/misc/examples/rawptr_elements.c @@ -0,0 +1,55 @@ +#include <stc/ccommon.h> +#include <stdio.h> + +#include <stc/cstr.h> +// Map of cstr => int64 pointers +typedef int64_t inttype; + +// Do it without cbox: +#define i_type SIPtrMap +#define i_key_str +#define i_val inttype* +#define i_valraw inttype +#define i_valfrom(raw) c_new(inttype, raw) +#define i_valto(x) **x +#define i_valclone(x) c_new(inttype, *x) +#define i_valdrop(x) c_free(*x) +#include <stc/cmap.h> + +// With cbox: +#define i_type IBox +#define i_val int +#include <stc/cbox.h> //<stc/carc.h> + +#define i_type SIBoxMap +#define i_key_str +#define i_valboxed IBox +#include <stc/cmap.h> + +int main() +{ + c_auto (SIPtrMap, map, m1) + c_auto (SIBoxMap, m2) + { + printf("\nMap with pointer elements:\n"); + SIPtrMap_insert(&map, cstr_from("testing"), c_new(inttype, 1)); + SIPtrMap_insert(&map, cstr_from("done"), c_new(inttype, 2)); + + // Emplace: implicit key, val construction: + SIPtrMap_emplace(&map, "hello", 3); + SIPtrMap_emplace(&map, "goodbye", 4); + + m1 = SIPtrMap_clone(map); + + c_forpair (name, number, SIPtrMap, m1) + printf("%s: %" PRId64 "\n", cstr_str(_.name), **_.number); + + + puts("\nIBox map:"); + SIBoxMap_insert(&m2, cstr_from("Hello"), IBox_make(123)); + SIBoxMap_emplace(&m2, "World", 999); + c_forpair (name, number, SIBoxMap, m2) + printf("%s: %d\n", cstr_str(_.name), *_.number->get); + puts(""); + } +} diff --git a/misc/examples/read.c b/misc/examples/read.c new file mode 100644 index 00000000..4efdcfeb --- /dev/null +++ b/misc/examples/read.c @@ -0,0 +1,25 @@ +#include <stc/cstr.h> +#define i_val_str +#include <stc/cvec.h> +#include <errno.h> + +cvec_str read_file(const char* name) +{ + cvec_str vec = cvec_str_init(); + c_with (FILE* f = fopen(name, "r"), fclose(f)) + c_with (cstr line = cstr_NULL, cstr_drop(&line)) + while (cstr_getline(&line, f)) + cvec_str_push(&vec, cstr_clone(line)); + return vec; +} + +int main() +{ + int n = 0; + c_with (cvec_str vec = read_file(__FILE__), cvec_str_drop(&vec)) + c_foreach (i, cvec_str, vec) + printf("%5d: %s\n", ++n, cstr_str(i.ref)); + + if (errno) + printf("error: read_file(" __FILE__ "). errno: %d\n", errno); +} diff --git a/misc/examples/regex1.c b/misc/examples/regex1.c new file mode 100644 index 00000000..7a22220d --- /dev/null +++ b/misc/examples/regex1.c @@ -0,0 +1,30 @@ +#define i_extern +#include <stc/cregex.h> + +int main(int argc, char* argv[]) +{ + if (argc <= 1) { + printf("Usage: regex1 -i\n"); + return 0; + } + c_auto (cstr, input) + c_auto (cregex, float_expr) + { + int res = cregex_compile(&float_expr, "^[+-]?[0-9]+((\\.[0-9]*)?|\\.[0-9]+)$", CREG_DEFAULT); + // Until "q" is given, ask for another number + if (res > 0) while (true) + { + printf("Enter a double precision number (q for quit): "); + cstr_getline(&input, stdin); + + // Exit when the user inputs q + if (cstr_equals(&input, "q")) + break; + + if (cregex_is_match(&float_expr, cstr_str(&input))) + printf("Input is a float\n"); + else + printf("Invalid input : Not a float\n"); + } + } +} diff --git a/misc/examples/regex2.c b/misc/examples/regex2.c new file mode 100644 index 00000000..dbf6aae4 --- /dev/null +++ b/misc/examples/regex2.c @@ -0,0 +1,32 @@ +#define i_extern +#include <stc/cregex.h> + +int main() +{ + struct { const char *pattern, *input; } s[] = { + {"(\\d\\d\\d\\d)[-_](1[0-2]|0[1-9])[-_](3[01]|[12][0-9]|0[1-9])", + "date: 2024-02-29 leapyear day, christmas eve is on 2022-12-24." + }, + {"(https?://|ftp://|www\\.)([0-9A-Za-z@:%_+~#=-]+\\.)+([a-z][a-z][a-z]?)(/[/0-9A-Za-z\\.@:%_+~#=\\?&-]*)?", + "https://en.cppreference.com/w/cpp/regex/regex_search" + }, + {"!((abc|123)+)!", "!123abcabc!"} + }; + + c_auto (cregex, re) + c_forrange (i, c_arraylen(s)) + { + int res = cregex_compile(&re, s[i].pattern, CREG_DEFAULT); + if (res < 0) { + printf("error in regex pattern: %d\n", res); + continue; + } + printf("input: %s\n", s[i].input); + + c_formatch (j, &re, s[i].input) { + c_forrange (k, cregex_captures(&re)) + printf(" submatch %lld: %.*s\n", k, c_ARGSV(j.match[k])); + puts(""); + } + } +} diff --git a/misc/examples/regex_match.c b/misc/examples/regex_match.c new file mode 100644 index 00000000..376b002e --- /dev/null +++ b/misc/examples/regex_match.c @@ -0,0 +1,34 @@ +#define i_extern +#include <stc/cregex.h> +#include <stc/csview.h> +#define i_val float +#include <stc/cstack.h> + +int main() +{ + // Lets find the first sequence of digits in a string + const char *str = "Hello numeric world, there are 24 hours in a day, 3600 seconds in an hour." + " Around 365.25 days a year, and 52 weeks in a year." + " Boltzmann const: 1.38064852E-23, is very small." + " Bohrradius is 5.29177210903e-11, and Avogadros number is 6.02214076e23."; + + c_auto (cregex, re) + c_auto (cstack_float, vec) + c_auto (cstr, nums) + { + const char* pattern = "[+-]?([0-9]*\\.)?\\d+([Ee][+-]?\\d+)?"; + int res = cregex_compile(&re, pattern, CREG_DEFAULT); + printf("%d: %s\n", res, pattern); + + // extract and convert all numbers in str to floats + c_formatch (i, &re, str) + cstack_float_push(&vec, atof(i.match[0].str)); + + c_foreach (i, cstack_float, vec) + printf(" %g\n", *i.ref); + + // extracts the numbers only to a comma separated string. + nums = cregex_replace_sv(&re, csview_from(str), " $0,", 0, NULL, CREG_R_STRIP); + printf("\n%s\n", cstr_str(&nums)); + } +} diff --git a/misc/examples/regex_replace.c b/misc/examples/regex_replace.c new file mode 100644 index 00000000..e296dfd8 --- /dev/null +++ b/misc/examples/regex_replace.c @@ -0,0 +1,53 @@ +#define i_extern +#include <stc/cregex.h> +#include <stc/csview.h> + +bool add_10_years(int i, csview match, cstr* out) { + if (i == 1) { // group 1 matches year + int year; + sscanf(match.str, "%4d", &year); // scan 4 chars only + cstr_printf(out, "%04d", year + 10); + return true; + } + return false; +} + +int main() +{ + const char* pattern = "\\b(\\d\\d\\d\\d)-(1[0-2]|0[1-9])-(3[01]|[12][0-9]|0[1-9])\\b"; + const char* input = "start date: 2015-12-31, end date: 2022-02-28"; + + c_auto (cstr, str) + { + printf("INPUT: %s\n", input); + + /* replace with a fixed string, extended all-in-one call: */ + cstr_take(&str, cregex_replace_pattern(pattern, input, "YYYY-MM-DD")); + printf("fixed: %s\n", cstr_str(&str)); + + /* US date format, and add 10 years to dates: */ + cstr_take(&str, cregex_replace_pattern_n(pattern, input, "$1/$3/$2", 0, add_10_years, CREG_DEFAULT)); + printf("us+10: %s\n", cstr_str(&str)); + + /* Wrap first date inside []: */ + cstr_take(&str, cregex_replace_pattern_n(pattern, input, "[$0]", 1, NULL, CREG_DEFAULT)); + printf("brack: %s\n", cstr_str(&str)); + + /* Shows how to compile RE separately */ + c_with (cregex re = cregex_from(pattern, CREG_DEFAULT), cregex_drop(&re)) { + if (cregex_captures(&re) == 0) + continue; /* break c_with */ + /* European date format. */ + cstr_take(&str, cregex_replace(&re, input, "$3.$2.$1")); + printf("euros: %s\n", cstr_str(&str)); + + /* Strip out everything but the matches */ + cstr_take(&str, cregex_replace_sv(&re, csview_from(input), "$3.$2.$1;", 0, NULL, CREG_R_STRIP)); + printf("strip: %s\n", cstr_str(&str)); + } + + /* Wrap all words in ${} */ + cstr_take(&str, cregex_replace_pattern("[a-z]+", "52 apples and 31 mangoes", "$${$0}")); + printf("curly: %s\n", cstr_str(&str)); + } +} diff --git a/misc/examples/replace.c b/misc/examples/replace.c new file mode 100644 index 00000000..15cf3bae --- /dev/null +++ b/misc/examples/replace.c @@ -0,0 +1,34 @@ +#include <stc/cstr.h> + +int main () +{ + const char *base = "this is a test string."; + const char *s2 = "n example"; + const char *s3 = "sample phrase"; + + // replace signatures used in the same order as described above: + + // Ustring positions: 0123456789*123456789*12345 + cstr s = cstr_from(base); // "this is a test string." + cstr m = cstr_clone(s); + c_defer (cstr_drop(&s), cstr_drop(&m)) { + cstr_append(&m, cstr_str(&m)); + cstr_append(&m, cstr_str(&m)); + printf("%s\n", cstr_str(&m)); + + cstr_replace_at(&s, 9, 5, s2); // "this is an example string." (1) + printf("(1) %s\n", cstr_str(&s)); + + cstr_replace_at_sv(&s, 19, 6, c_SV(s3+7, 6)); // "this is an example phrase." (2) + printf("(2) %s\n", cstr_str(&s)); + + cstr_replace_at(&s, 8, 10, "just a"); // "this is just a phrase." (3) + printf("(3) %s\n", cstr_str(&s)); + + cstr_replace_at_sv(&s, 8, 6, c_SV("a shorty", 7)); // "this is a short phrase." (4) + printf("(4) %s\n", cstr_str(&s)); + + cstr_replace_at(&s, 22, 1, "!!!"); // "this is a short phrase!!!" (5) + printf("(5) %s\n", cstr_str(&s)); + } +} diff --git a/misc/examples/shape.c b/misc/examples/shape.c new file mode 100644 index 00000000..a17208ef --- /dev/null +++ b/misc/examples/shape.c @@ -0,0 +1,159 @@ +// Demo of typesafe polymorphism in C99, using STC. + +#include <stdlib.h> +#include <stdio.h> +#include <stc/ccommon.h> + +#define c_dyn_cast(T, s) \ + (&T##_api == (s)->api ? (T*)(s) : (T*)0) + +// Shape definition +// ============================================================ + +typedef struct { + float x, y; +} Point; + +typedef struct Shape Shape; + +struct ShapeAPI { + void (*drop)(Shape*); + void (*draw)(const Shape*); +}; + +struct Shape { + struct ShapeAPI* api; + uint32_t color; + uint16_t style; + uint8_t thickness; + uint8_t hardness; +}; + +void Shape_drop(Shape* shape) +{ + printf("shape destructed\n"); +} + +void Shape_delete(Shape* shape) +{ + if (shape) { + shape->api->drop(shape); + c_free(shape); + } +} + +// Triangle implementation +// ============================================================ + +typedef struct { + Shape shape; + Point p[3]; +} Triangle; + +static struct ShapeAPI Triangle_api; + + +Triangle Triangle_from(Point a, Point b, Point c) +{ + Triangle t = {{.api=&Triangle_api}, .p={a, b, c}}; + return t; +} + +static void Triangle_draw(const Shape* shape) +{ + const Triangle* self = c_dyn_cast(Triangle, shape); + printf("Triangle : (%g,%g), (%g,%g), (%g,%g)\n", + self->p[0].x, self->p[0].y, + self->p[1].x, self->p[1].y, + self->p[2].x, self->p[2].y); +} + +static struct ShapeAPI Triangle_api = { + .drop = Shape_drop, + .draw = Triangle_draw, +}; + +// Polygon implementation +// ============================================================ + +#define i_type PointVec +#define i_val Point +#include <stc/cstack.h> + +typedef struct { + Shape shape; + PointVec points; +} Polygon; + +static struct ShapeAPI Polygon_api; + + +Polygon Polygon_init(void) +{ + Polygon p = {{.api=&Polygon_api}, .points=PointVec_init()}; + return p; +} + +void Polygon_addPoint(Polygon* self, Point p) +{ + PointVec_push(&self->points, p); +} + +static void Polygon_drop(Shape* shape) +{ + Polygon* self = c_dyn_cast(Polygon, shape); + printf("poly destructed\n"); + PointVec_drop(&self->points); + Shape_drop(shape); +} + +static void Polygon_draw(const Shape* shape) +{ + const Polygon* self = c_dyn_cast(Polygon, shape); + printf("Polygon :"); + c_foreach (i, PointVec, self->points) + printf(" (%g,%g)", i.ref->x, i.ref->y); + puts(""); +} + +static struct ShapeAPI Polygon_api = { + .drop = Polygon_drop, + .draw = Polygon_draw, +}; + +// Test +// ============================================================ + +#define i_type Shapes +#define i_val Shape* +#define i_valdrop(x) Shape_delete(*x) +#include <stc/cstack.h> + +void testShape(const Shape* shape) +{ + shape->api->draw(shape); +} + + +int main(void) +{ + c_auto (Shapes, shapes) + { + Triangle* tri1 = c_new(Triangle, Triangle_from((Point){5, 7}, (Point){12, 7}, (Point){12, 20})); + Polygon* pol1 = c_new(Polygon, Polygon_init()); + Polygon* pol2 = c_new(Polygon, Polygon_init()); + + c_forlist (i, Point, {{50, 72}, {123, 73}, {127, 201}, {828, 333}}) + Polygon_addPoint(pol1, *i.ref); + + c_forlist (i, Point, {{5, 7}, {12, 7}, {12, 20}, {82, 33}, {17, 56}}) + Polygon_addPoint(pol2, *i.ref); + + Shapes_push(&shapes, &tri1->shape); + Shapes_push(&shapes, &pol1->shape); + Shapes_push(&shapes, &pol2->shape); + + c_foreach (i, Shapes, shapes) + testShape(*i.ref); + } +} diff --git a/misc/examples/shape.cpp b/misc/examples/shape.cpp new file mode 100644 index 00000000..ea1f53d2 --- /dev/null +++ b/misc/examples/shape.cpp @@ -0,0 +1,122 @@ +// Demo of polymorphism in C++ + +#include <iostream> +#include <memory> +#include <vector> + +// Shape definition +// ============================================================ + +struct Point { + float x, y; +}; + +std::ostream& operator<<(std::ostream& os, const Point& p) { + os << " (" << p.x << "," << p.y << ")"; + return os; +} + +struct Shape { + virtual ~Shape(); + virtual void draw() const = 0; + + uint32_t color; + uint16_t style; + uint8_t thickness; + uint8_t hardness; +}; + +Shape::~Shape() +{ + std::cout << "base destructed" << std::endl; +} + +// Triangle implementation +// ============================================================ + +struct Triangle : public Shape +{ + Triangle(Point a, Point b, Point c); + void draw() const override; + + private: Point p[3]; +}; + + +Triangle::Triangle(Point a, Point b, Point c) + : p{a, b, c} {} + +void Triangle::draw() const +{ + std::cout << "Triangle :" + << p[0] << p[1] << p[2] + << std::endl; +} + + +// Polygon implementation +// ============================================================ + + +struct Polygon : public Shape +{ + ~Polygon(); + void draw() const override; + void addPoint(const Point& p); + + private: std::vector<Point> points; +}; + + +void Polygon::addPoint(const Point& p) +{ + points.push_back(p); +} + +Polygon::~Polygon() +{ + std::cout << "poly destructed" << std::endl; +} + +void Polygon::draw() const +{ + std::cout << "Polygon :"; + for (auto& p : points) + std::cout << p ; + std::cout << std::endl; +} + + +// Test +// ============================================================ + +void testShape(const Shape* shape) +{ + shape->draw(); +} + +#include <array> + +int main(void) +{ + std::vector<std::unique_ptr<Shape>> shapes; + + auto tri1 = std::make_unique<Triangle>(Point{5, 7}, Point{12, 7}, Point{12, 20}); + auto pol1 = std::make_unique<Polygon>(); + auto pol2 = std::make_unique<Polygon>(); + + for (auto& p: std::array<Point, 4> + {{{50, 72}, {123, 73}, {127, 201}, {828, 333}}}) + pol1->addPoint(p); + + for (auto& p: std::array<Point, 5> + {{{5, 7}, {12, 7}, {12, 20}, {82, 33}, {17, 56}}}) + pol2->addPoint(p); + + shapes.push_back(std::move(tri1)); + shapes.push_back(std::move(pol1)); + shapes.push_back(std::move(pol2)); + + for (auto& shape: shapes) + testShape(shape.get()); +} diff --git a/misc/examples/sidebyside.cpp b/misc/examples/sidebyside.cpp new file mode 100644 index 00000000..80c934a4 --- /dev/null +++ b/misc/examples/sidebyside.cpp @@ -0,0 +1,57 @@ +#include <iostream> +#include <map> +#include <string> +#include <stc/cstr.h> + +#define i_type IIMap +#define i_key int +#define i_val int +#include <stc/csmap.h> + +#define i_type SIMap +#define i_key_str +#define i_val int +#include <stc/cmap.h> + +int main() { + { + std::map<int, int> hist; + hist.emplace(12, 100).first->second += 1; + hist.emplace(13, 100).first->second += 1; + hist.emplace(12, 100).first->second += 1; + + for (auto i: hist) + std::cout << i.first << ", " << i.second << std::endl; + std::cout << std::endl; + } + + c_auto (IIMap, hist) + { + IIMap_insert(&hist, 12, 100).ref->second += 1; + IIMap_insert(&hist, 13, 100).ref->second += 1; + IIMap_insert(&hist, 12, 100).ref->second += 1; + + c_foreach (i, IIMap, hist) + printf("%d, %d\n", i.ref->first, i.ref->second); + puts(""); + } + // =================================================== + { + std::map<std::string, int> food = + {{"burger", 5}, {"pizza", 12}, {"steak", 15}}; + + for (auto i: food) + std::cout << i.first << ", " << i.second << std::endl; + std::cout << std::endl; + } + + c_auto (SIMap, food) + { + c_forlist (i, SIMap_raw, {{"burger", 5}, {"pizza", 12}, {"steak", 15}}) + SIMap_emplace(&food, c_PAIR(i.ref)); + + c_foreach (i, SIMap, food) + printf("%s, %d\n", cstr_str(&i.ref->first), i.ref->second); + puts(""); + } +} diff --git a/misc/examples/sorted_map.c b/misc/examples/sorted_map.c new file mode 100644 index 00000000..c4a05c76 --- /dev/null +++ b/misc/examples/sorted_map.c @@ -0,0 +1,64 @@ +// https://iq.opengenus.org/containers-cpp-stl/ + +#define i_key int +#define i_val int +#include <stc/csmap.h> +#include <stdio.h> + +int main() +{ + + // empty map containers + c_auto (csmap_int, gquiz1, gquiz2) + { + // insert elements in random order + csmap_int_insert(&gquiz1, 2, 30); + csmap_int_insert(&gquiz1, 4, 20); + csmap_int_insert(&gquiz1, 7, 10); + csmap_int_insert(&gquiz1, 5, 50); + csmap_int_insert(&gquiz1, 3, 60); + csmap_int_insert(&gquiz1, 1, 40); + csmap_int_insert(&gquiz1, 6, 50); + + // printing map gquiz1 + printf("\nThe map gquiz1 is :\n\tKEY\tELEMENT\n"); + c_foreach (itr, csmap_int, gquiz1) + printf("\t%d\t%d\n", itr.ref->first, itr.ref->second); + printf("\n"); + + // assigning the elements from gquiz1 to gquiz2 + c_foreach (i, csmap_int, gquiz1) + csmap_int_insert(&gquiz2, i.ref->first, i.ref->second); + + // print all elements of the map gquiz2 + printf("\nThe map gquiz2 is :\n\tKEY\tELEMENT\n"); + c_foreach (itr, csmap_int, gquiz2) + printf("\t%d\t%d\n", itr.ref->first, itr.ref->second); + printf("\n"); + + // remove all elements up to element with key=3 in gquiz2 + printf("\ngquiz2 after removal of elements less than key=3 :\n"); + printf("\tKEY\tELEMENT\n"); + csmap_int_erase_range(&gquiz2, csmap_int_begin(&gquiz2), + csmap_int_find(&gquiz2, 3)); + c_foreach (itr, csmap_int, gquiz2) + printf("\t%d\t%d\n", itr.ref->first, itr.ref->second); + printf("\n"); + + // remove all elements with key = 4 + int num = csmap_int_erase(&gquiz2, 4); + printf("\ngquiz2.erase(4) : %d removed\n", num); + printf("\tKEY\tELEMENT\n"); + c_foreach (itr, csmap_int, gquiz2) + printf("\t%d\t%d\n", itr.ref->first, itr.ref->second); + printf("\n"); + + // lower bound and upper bound for map gquiz1 key = 5 + printf("gquiz1.lower_bound(5) : "); + printf("\tKEY = %d\t", csmap_int_lower_bound(&gquiz1, 5).ref->first); + printf("\tELEMENT = %d\n", csmap_int_lower_bound(&gquiz1, 5).ref->second); + printf("gquiz1.upper_bound(5) : "); + printf("\tKEY = %d\t", csmap_int_lower_bound(&gquiz1, 5+1).ref->first); + printf("\tELEMENT = %d\n", csmap_int_lower_bound(&gquiz1, 5+1).ref->second); + } +} diff --git a/misc/examples/splitstr.c b/misc/examples/splitstr.c new file mode 100644 index 00000000..a3c12a3a --- /dev/null +++ b/misc/examples/splitstr.c @@ -0,0 +1,19 @@ +#include <stdio.h> +#define i_extern // cstr + utf8 functions +#include <stc/cregex.h> +#include <stc/csview.h> + +int main() +{ + puts("Split with c_fortoken (csview):"); + + c_fortoken (i, "Hello World C99!", " ") + printf("'%.*s'\n", c_ARGSV(i.token)); + + + puts("\nSplit with c_formatch (regex):"); + + c_with (cregex re = cregex_from("[^ ]+", CREG_DEFAULT), cregex_drop(&re)) + c_formatch (i, &re, " Hello World C99! ") + printf("'%.*s'\n", c_ARGSV(i.match[0])); +} diff --git a/misc/examples/sso_map.c b/misc/examples/sso_map.c new file mode 100644 index 00000000..cc5e16a1 --- /dev/null +++ b/misc/examples/sso_map.c @@ -0,0 +1,17 @@ +#include <stc/cstr.h> +#define i_key_str +#define i_val_str +#include <stc/cmap.h> + +int main() +{ + c_auto (cmap_str, m) { + cmap_str_emplace(&m, "Test short", "This is a short string."); + cmap_str_emplace(&m, "Test long ", "This is a longer string."); + + c_forpair (k, v, cmap_str, m) + printf("%s: '%s' Len=%" c_ZU ", Is long: %s\n", + cstr_str(_.k), cstr_str(_.v), cstr_size(_.v), + cstr_is_long(_.v)?"true":"false"); + } +} diff --git a/misc/examples/sso_substr.c b/misc/examples/sso_substr.c new file mode 100644 index 00000000..be372a8d --- /dev/null +++ b/misc/examples/sso_substr.c @@ -0,0 +1,20 @@ +#define STC_IMPLEMENT +#include <stc/cstr.h> +#include <stc/csview.h> + +int main () +{ + cstr str = cstr_lit("We think in generalities, but we live in details."); + csview sv1 = cstr_substr_ex(&str, 3, 5); // "think" + size_t pos = cstr_find(&str, "live"); // position of "live" + csview sv2 = cstr_substr_ex(&str, pos, 4); // "live" + csview sv3 = cstr_slice_ex(&str, -8, -1); // "details" + printf("%.*s, %.*s, %.*s\n", c_ARGSV(sv1), c_ARGSV(sv2), c_ARGSV(sv3)); + + cstr_assign(&str, "apples are green or red"); + cstr s2 = cstr_from_sv(cstr_substr_ex(&str, -3, 3)); // "red" + cstr s3 = cstr_from_sv(cstr_substr_ex(&str, 0, 6)); // "apples" + printf("%s %s: %d, %d\n", cstr_str(&s2), cstr_str(&s3), + cstr_is_long(&str), cstr_is_long(&s2)); + c_drop (cstr, &str, &s2, &s3); +} diff --git a/misc/examples/stack.c b/misc/examples/stack.c new file mode 100644 index 00000000..a6a4a492 --- /dev/null +++ b/misc/examples/stack.c @@ -0,0 +1,30 @@ + +#include <stdio.h> + +#define i_tag i +#define i_capacity 100 +#define i_val int +#include <stc/cstack.h> + +#define i_tag c +#define i_val char +#include <stc/cstack.h> + +int main() { + c_auto (cstack_i, stack) + c_auto (cstack_c, chars) + { + c_forrange (i, 101) + cstack_i_push(&stack, i*i); + + printf("%d\n", *cstack_i_top(&stack)); + + c_forrange (i, 90) + cstack_i_pop(&stack); + + c_foreach (i, cstack_i, stack) + printf(" %d", *i.ref); + puts(""); + printf("top: %d\n", *cstack_i_top(&stack)); + } +} diff --git a/misc/examples/sview_split.c b/misc/examples/sview_split.c new file mode 100644 index 00000000..d22fccd1 --- /dev/null +++ b/misc/examples/sview_split.c @@ -0,0 +1,20 @@ +#define STC_IMPLEMENT +#include <stc/cstr.h> +#include <stc/csview.h> + +int main() +{ + // No memory allocations or string length calculations! + const csview date = c_SV("2021/03/12"); + size_t pos = 0; + const csview year = csview_token(date, "/", &pos); + const csview month = csview_token(date, "/", &pos); + const csview day = csview_token(date, "/", &pos); + + printf("%.*s, %.*s, %.*s\n", c_ARGSV(year), c_ARGSV(month), c_ARGSV(day)); + + c_auto (cstr, y, m, d) { + y = cstr_from_sv(year), m = cstr_from_sv(month), d = cstr_from_sv(day); + printf("%s, %s, %s\n", cstr_str(&y), cstr_str(&m), cstr_str(&d)); + } +} diff --git a/misc/examples/unordered_set.c b/misc/examples/unordered_set.c new file mode 100644 index 00000000..a0b639da --- /dev/null +++ b/misc/examples/unordered_set.c @@ -0,0 +1,42 @@ +// https://iq.opengenus.org/containers-cpp-stl/ +// C program to demonstrate various function of stc cset +#include <stc/cstr.h> +#define i_key_str +#include <stc/cset.h> + +int main() +{ + // declaring set for storing string data-type + c_auto (cset_str, stringSet) + { + // inserting various string, same string will be stored + // once in set + cset_str_emplace(&stringSet, "code"); + cset_str_emplace(&stringSet, "in"); + cset_str_emplace(&stringSet, "C"); + cset_str_emplace(&stringSet, "is"); + cset_str_emplace(&stringSet, "fast"); + + const char* key = "slow"; + + // find returns end iterator if key is not found, + // else it returns iterator to that key + + if (cset_str_find(&stringSet, key).ref == cset_str_end(&stringSet).ref) + printf("\"%s\" not found\n", key); + else + printf("Found \"%s\"\n", key); + + key = "C"; + if (!cset_str_contains(&stringSet, key)) + printf("\"%s\" not found\n", key); + else + printf("Found \"%s\"\n", key); + + // now iterating over whole set and printing its + // content + printf("All elements :\n"); + c_foreach (itr, cset_str, stringSet) + printf("%s\n", cstr_str(itr.ref)); + } +} diff --git a/misc/examples/utf8replace_c.c b/misc/examples/utf8replace_c.c new file mode 100644 index 00000000..22a5c990 --- /dev/null +++ b/misc/examples/utf8replace_c.c @@ -0,0 +1,23 @@ +#define i_extern // add utf8 dependencies +#include <stc/cstr.h> +#include <stc/csview.h> + +int main() { + c_auto (cstr, hello, upper) { + hello = cstr_lit("hell😀 w😀rld"); + printf("%s\n", cstr_str(&hello)); + + /* replace second smiley at utf8 codepoint pos 7 */ + cstr_u8_replace(&hello, cstr_u8_to_pos(&hello, 7), 1, c_SV("🐨")); + printf("%s\n", cstr_str(&hello)); + + cstr_replace(&hello, "🐨", "ø", 1); + printf("%s\n", cstr_str(&hello)); + + upper = cstr_toupper_sv(cstr_sv(&hello)); + + c_foreach (c, cstr, hello) + printf("%.*s,", c_ARGSV(c.u8.chr)); + puts(""); + } +} diff --git a/misc/examples/utf8replace_rs.rs b/misc/examples/utf8replace_rs.rs new file mode 100644 index 00000000..717978aa --- /dev/null +++ b/misc/examples/utf8replace_rs.rs @@ -0,0 +1,19 @@ + +pub fn main() { + let mut hello = String::from("hell😀 world"); + println!("{}", hello); + + hello.replace_range( + hello + .char_indices() + .nth(4) + .map(|(pos, ch)| (pos..pos + ch.len_utf8())) + .unwrap(), + "🐨", + ); + println!("{}", hello); + + for c in hello.chars() { + print!("{},", c); + } +} diff --git a/misc/examples/vikings.c b/misc/examples/vikings.c new file mode 100644 index 00000000..26a74757 --- /dev/null +++ b/misc/examples/vikings.c @@ -0,0 +1,66 @@ +#include <stc/cstr.h> + +typedef struct Viking { + cstr name; + cstr country; +} Viking; + +void Viking_drop(Viking* vk) { + cstr_drop(&vk->name); + cstr_drop(&vk->country); +} + +// Define Viking lookup struct with hash, cmp, and convertion functions between Viking and RViking structs: + +typedef struct RViking { + const char* name; + const char* country; +} RViking; + +static inline int RViking_cmp(const RViking* rx, const RViking* ry) { + int c = strcmp(rx->name, ry->name); + return c ? c : strcmp(rx->country, ry->country); +} + +static inline Viking Viking_from(RViking raw) { // note: parameter is by value + return c_INIT(Viking){cstr_from(raw.name), cstr_from(raw.country)}; +} + +static inline RViking Viking_toraw(const Viking* vp) { + return c_INIT(RViking){cstr_str(&vp->name), cstr_str(&vp->country)}; +} + +// With this in place, we define the Viking => int hash map type: +#define i_type Vikings +#define i_keyclass Viking // key type +#define i_rawclass RViking // lookup type +#define i_keyfrom Viking_from +#define i_opt c_no_clone +#define i_hash(rp) cstrhash(rp->name) ^ cstrhash(rp->country) +#define i_val int // mapped type +#include <stc/cmap.h> +/* + i_keyclass implies these defines, unless they are already defined: + i_cmp => RViking_cmp + //i_hash => RViking_hash // already defined. + //i_keyclone => Viking_clone // not used, because of c_no_clone + i_keyto => Viking_toraw // because i_rawclass is defined + i_keydrop => Viking_drop +*/ + +int main() +{ + c_auto (Vikings, vikings) { + Vikings_emplace(&vikings, (RViking){"Einar", "Norway"}, 20); + Vikings_emplace(&vikings, (RViking){"Olaf", "Denmark"}, 24); + Vikings_emplace(&vikings, (RViking){"Harald", "Iceland"}, 12); + Vikings_emplace(&vikings, (RViking){"Björn", "Sweden"}, 10); + + Vikings_value* v = Vikings_get_mut(&vikings, (RViking){"Einar", "Norway"}); + v->second += 3; // add 3 hp points to Einar + + c_forpair (vk, hp, Vikings, vikings) { + printf("%s of %s has %d hp\n", cstr_str(&_.vk->name), cstr_str(&_.vk->country), *_.hp); + } + } +} diff --git a/misc/examples/words.c b/misc/examples/words.c new file mode 100644 index 00000000..097447aa --- /dev/null +++ b/misc/examples/words.c @@ -0,0 +1,68 @@ +#include <math.h> +#include <stc/cstr.h> + +#define i_val_str +#include <stc/cvec.h> + +#define i_key_str +#define i_val int +#include <stc/cmap.h> + +int main1() +{ + c_auto (cvec_str, words) + c_auto (cmap_str, word_map) + { + c_forlist (i, const char*, { + "this", "sentence", "is", "not", "a", "sentence", + "this", "sentence", "is", "a", "hoax" + }) cvec_str_emplace_back(&words, *i.ref); + + c_foreach (w, cvec_str, words) { + cmap_str_emplace(&word_map, cstr_str(w.ref), 0).ref->second += 1; + } + + c_foreach (i, cmap_str, word_map) { + printf("%d occurrences of word '%s'\n", + i.ref->second, cstr_str(&i.ref->first)); + } + } + return 0; +} + +#ifdef __cplusplus +#include <string> +#include <iostream> +#include <vector> +#include <unordered_map> + +int main2() +{ + std::vector<std::string> words = { + "this", "sentence", "is", "not", "a", "sentence", + "this", "sentence", "is", "a", "hoax" + }; + + std::unordered_map<std::string, size_t> word_map; + for (const auto &w : words) { + word_map[w] += 1; + } + + for (const auto &pair : word_map) { + std::cout << pair.second + << " occurrences of word '" + << pair.first << "'\n"; + } + return 0; +} + +int main() { + main1(); + puts(""); + main2(); +} +#else +int main() { + main1(); +} +#endif diff --git a/misc/include/alt/csmap.h b/misc/include/alt/csmap.h new file mode 100644 index 00000000..4b4e764c --- /dev/null +++ b/misc/include/alt/csmap.h @@ -0,0 +1,512 @@ +/* MIT License + * + * Copyright (c) 2022 Tyge Løvset, NORCE, www.norceresearch.no + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// Sorted/Ordered set and map - implemented as an AA-tree. +/* +#include <stdio.h> +#include <stc/cstr.h> + +#define i_tag sx // Sorted map<cstr, double> +#define i_key_str +#define i_val double +#include <stc/csmap.h> + +int main(void) { + c_with (csmap_sx m = csmap_sx_init(), csmap_sx_drop(&m)) + { + csmap_sx_emplace(&m, "Testing one", 1.234); + csmap_sx_emplace(&m, "Testing two", 12.34); + csmap_sx_emplace(&m, "Testing three", 123.4); + + csmap_sx_value *v = csmap_sx_get(&m, "Testing five"); // NULL + double num = *csmap_sx_at(&m, "Testing one"); + csmap_sx_emplace_or_assign(&m, "Testing three", 1000.0); // update + csmap_sx_erase(&m, "Testing two"); + + c_foreach (i, csmap_sx, m) + printf("map %s: %g\n", cstr_str(&i.ref->first), i.ref->second); + } +} +*/ +#include <stc/ccommon.h> + +#ifndef CSMAP_H_INCLUDED +#define STC_CSMAP_V1 1 +#include <stc/forward.h> +#include <stdlib.h> +#include <string.h> +#endif // CSMAP_H_INCLUDED + +#ifndef _i_prefix +#define _i_prefix csmap_ +#endif +#ifdef _i_isset + #define _i_MAP_ONLY c_false + #define _i_SET_ONLY c_true + #define _i_keyref(vp) (vp) +#else + #define _i_ismap + #define _i_MAP_ONLY c_true + #define _i_SET_ONLY c_false + #define _i_keyref(vp) (&(vp)->first) +#endif +#include <stc/priv/template.h> + +#if !c_option(c_is_forward) +_cx_deftypes(_c_aatree_types, _cx_self, i_key, i_val, i_size, _i_MAP_ONLY, _i_SET_ONLY); +#endif + +_i_MAP_ONLY( struct _cx_value { + _cx_key first; + _cx_mapped second; +}; ) +struct _cx_node { + struct _cx_node *link[2]; + uint8_t level; + _cx_value value; +}; + +typedef i_keyraw _cx_rawkey; +typedef i_valraw _cx_memb(_rawmapped); +typedef _i_SET_ONLY( i_keyraw ) + _i_MAP_ONLY( struct { i_keyraw first; i_valraw second; } ) + _cx_raw; + +#if !defined i_no_clone +STC_API _cx_self _cx_memb(_clone)(_cx_self cx); +#if !defined i_no_emplace +STC_API _cx_result _cx_memb(_emplace)(_cx_self* self, i_keyraw rkey _i_MAP_ONLY(, i_valraw rmapped)); +#endif // !i_no_emplace +#endif // !i_no_clone +STC_API _cx_self _cx_memb(_init)(void); +STC_API _cx_result _cx_memb(_insert)(_cx_self* self, i_key key _i_MAP_ONLY(, i_val mapped)); +STC_API _cx_result _cx_memb(_push)(_cx_self* self, _cx_value _val); +STC_API void _cx_memb(_drop)(_cx_self* self); +STC_API _cx_value* _cx_memb(_find_it)(const _cx_self* self, i_keyraw rkey, _cx_iter* out); +STC_API _cx_iter _cx_memb(_lower_bound)(const _cx_self* self, i_keyraw rkey); +STC_API _cx_value* _cx_memb(_front)(const _cx_self* self); +STC_API _cx_value* _cx_memb(_back)(const _cx_self* self); +STC_API int _cx_memb(_erase)(_cx_self* self, i_keyraw rkey); +STC_API _cx_iter _cx_memb(_erase_at)(_cx_self* self, _cx_iter it); +STC_API _cx_iter _cx_memb(_erase_range)(_cx_self* self, _cx_iter it1, _cx_iter it2); +STC_API void _cx_memb(_next)(_cx_iter* it); + +STC_INLINE bool _cx_memb(_empty)(_cx_self cx) { return cx.size == 0; } +STC_INLINE size_t _cx_memb(_size)(_cx_self cx) { return cx.size; } +STC_INLINE void _cx_memb(_swap)(_cx_self* a, _cx_self* b) { c_swap(_cx_self, *a, *b); } +STC_INLINE _cx_iter _cx_memb(_find)(const _cx_self* self, i_keyraw rkey) + { _cx_iter it; _cx_memb(_find_it)(self, rkey, &it); return it; } +STC_INLINE bool _cx_memb(_contains)(const _cx_self* self, i_keyraw rkey) + { _cx_iter it; return _cx_memb(_find_it)(self, rkey, &it) != NULL; } +STC_INLINE const _cx_value* _cx_memb(_get)(const _cx_self* self, i_keyraw rkey) + { _cx_iter it; return _cx_memb(_find_it)(self, rkey, &it); } +STC_INLINE _cx_value* _cx_memb(_get_mut)(_cx_self* self, i_keyraw rkey) + { _cx_iter it; return _cx_memb(_find_it)(self, rkey, &it); } + +STC_INLINE void +_cx_memb(_clear)(_cx_self* self) + { _cx_memb(_drop)(self); *self = _cx_memb(_init)(); } + +STC_INLINE _cx_raw +_cx_memb(_value_toraw)(_cx_value* val) { + return _i_SET_ONLY( i_keyto(val) ) + _i_MAP_ONLY( c_INIT(_cx_raw){i_keyto((&val->first)), + i_valto((&val->second))} ); +} + +STC_INLINE int +_cx_memb(_value_cmp)(const _cx_value* x, const _cx_value* y) { + _cx_rawkey rx = i_keyto(_i_keyref(x)), ry = i_keyto(_i_keyref(y)); + return i_cmp((&rx), (&ry)); +} + +STC_INLINE void +_cx_memb(_value_drop)(_cx_value* val) { + i_keydrop(_i_keyref(val)); + _i_MAP_ONLY( i_valdrop((&val->second)); ) +} + +#if !defined i_no_clone +STC_INLINE _cx_value +_cx_memb(_value_clone)(_cx_value _val) { + *_i_keyref(&_val) = i_keyclone((*_i_keyref(&_val))); + _i_MAP_ONLY( _val.second = i_valclone(_val.second); ) + return _val; +} + +STC_INLINE void +_cx_memb(_copy)(_cx_self *self, const _cx_self* other) { + if (self->root == other->root) + return; + _cx_memb(_drop)(self); + *self = _cx_memb(_clone)(*other); +} +#endif // !i_no_clone + +#ifndef _i_isset + #if !defined i_no_clone && !defined i_no_emplace + STC_API _cx_result _cx_memb(_emplace_or_assign)(_cx_self* self, i_keyraw rkey, i_valraw rmapped); + #endif + STC_API _cx_result _cx_memb(_insert_or_assign)(_cx_self* self, i_key key, i_val mapped); + + STC_INLINE const _cx_mapped* + _cx_memb(_at)(const _cx_self* self, i_keyraw rkey) + { _cx_iter it; return &_cx_memb(_find_it)(self, rkey, &it)->second; } + STC_INLINE _cx_mapped* + _cx_memb(_at_mut)(_cx_self* self, i_keyraw rkey) + { _cx_iter it; return &_cx_memb(_find_it)(self, rkey, &it)->second; } +#endif // !_i_isset + +STC_INLINE _cx_iter +_cx_memb(_begin)(const _cx_self* self) { + _cx_iter it; + it.ref = NULL, it._top = 0, it._tn = self->root; + _cx_memb(_next)(&it); + return it; +} + +STC_INLINE _cx_iter +_cx_memb(_end)(const _cx_self* self) { + (void)self; + _cx_iter it; it.ref = NULL, it._top = 0, it._tn = NULL; + return it; +} + +STC_INLINE _cx_iter +_cx_memb(_advance)(_cx_iter it, size_t n) { + while (n-- && it.ref) + _cx_memb(_next)(&it); + return it; +} + +/* -------------------------- IMPLEMENTATION ------------------------- */ +#if defined(i_implement) + +#ifndef CSMAP_H_INCLUDED +static struct { void *link[2]; uint8_t level; } +_csmap_sentinel = {{&_csmap_sentinel, &_csmap_sentinel}, 0}; +#endif + +static _cx_result _cx_memb(_insert_entry_)(_cx_self* self, i_keyraw rkey); + +STC_DEF _cx_self +_cx_memb(_init)(void) { + _cx_self cx = {(_cx_node *)&_csmap_sentinel, 0}; + return cx; +} + +STC_DEF _cx_value* +_cx_memb(_front)(const _cx_self* self) { + _cx_node *tn = self->root; + while (tn->link[0]->level) + tn = tn->link[0]; + return &tn->value; +} + +STC_DEF _cx_value* +_cx_memb(_back)(const _cx_self* self) { + _cx_node *tn = self->root; + while (tn->link[1]->level) + tn = tn->link[1]; + return &tn->value; +} + +STC_DEF _cx_result +_cx_memb(_insert)(_cx_self* self, i_key key _i_MAP_ONLY(, i_val mapped)) { + _cx_result res = _cx_memb(_insert_entry_)(self, i_keyto((&key))); + if (res.inserted) + { *_i_keyref(res.ref) = key; _i_MAP_ONLY( res.ref->second = mapped; )} + else + { i_keydrop((&key)); _i_MAP_ONLY( i_valdrop((&mapped)); )} + return res; +} + +STC_DEF _cx_result +_cx_memb(_push)(_cx_self* self, _cx_value _val) { + _cx_result _res = _cx_memb(_insert_entry_)(self, i_keyto(_i_keyref(&_val))); + if (_res.inserted) + *_res.ref = _val; + else + _cx_memb(_value_drop)(&_val); + return _res; +} + +#ifndef _i_isset + STC_DEF _cx_result + _cx_memb(_insert_or_assign)(_cx_self* self, i_key key, i_val mapped) { + _cx_result res = _cx_memb(_insert_entry_)(self, i_keyto((&key))); + if (res.inserted) + res.ref->first = key; + else + { i_keydrop((&key)); i_valdrop((&res.ref->second)); } + res.ref->second = mapped; + return res; + } + #if !defined i_no_clone && !defined i_no_emplace + STC_DEF _cx_result + _cx_memb(_emplace_or_assign)(_cx_self* self, i_keyraw rkey, i_valraw rmapped) { + _cx_result res = _cx_memb(_insert_entry_)(self, rkey); + if (res.inserted) + res.ref->first = i_keyfrom(rkey); + else + { i_valdrop((&res.ref->second)); } + res.ref->second = i_valfrom(rmapped); + return res; + } + #endif // !i_no_clone && !i_no_emplace +#endif // !_i_isset + +STC_DEF _cx_value* +_cx_memb(_find_it)(const _cx_self* self, _cx_rawkey rkey, _cx_iter* out) { + _cx_node *tn = self->root; + out->_top = 0; + while (tn->level) { + int c; _cx_rawkey raw = i_keyto(_i_keyref(&tn->value)); + if ((c = i_cmp((&raw), (&rkey))) < 0) + tn = tn->link[1]; + else if (c > 0) + { out->_st[out->_top++] = tn; tn = tn->link[0]; } + else + { out->_tn = tn->link[1]; return (out->ref = &tn->value); } + } + return (out->ref = NULL); +} + +STC_DEF _cx_iter +_cx_memb(_lower_bound)(const _cx_self* self, i_keyraw rkey) { + _cx_iter it; + _cx_memb(_find_it)(self, rkey, &it); + if (!it.ref && it._top) { + _cx_node *tn = it._st[--it._top]; + it._tn = tn->link[1]; + it.ref = &tn->value; + } + return it; +} + +STC_DEF void +_cx_memb(_next)(_cx_iter *it) { + _cx_node *tn = it->_tn; + if (it->_top || tn->level) { + while (tn->level) { + it->_st[it->_top++] = tn; + tn = tn->link[0]; + } + tn = it->_st[--it->_top]; + it->_tn = tn->link[1]; + it->ref = &tn->value; + } else + it->ref = NULL; +} + +static _cx_node * +_cx_memb(_skew_)(_cx_node *tn) { + if (tn && tn->link[0]->level == tn->level && tn->level) { + _cx_node *tmp = tn->link[0]; + tn->link[0] = tmp->link[1]; + tmp->link[1] = tn; + tn = tmp; + } + return tn; +} + +static _cx_node * +_cx_memb(_split_)(_cx_node *tn) { + if (tn->link[1]->link[1]->level == tn->level && tn->level) { + _cx_node *tmp = tn->link[1]; + tn->link[1] = tmp->link[0]; + tmp->link[0] = tn; + tn = tmp; + ++tn->level; + } + return tn; +} + +static _cx_node* +_cx_memb(_insert_entry_i_)(_cx_node* tn, const _cx_rawkey* rkey, _cx_result* res) { + _cx_node *up[64], *tx = tn; + int c, top = 0, dir = 0; + while (tx->level) { + up[top++] = tx; + _cx_rawkey r = i_keyto(_i_keyref(&tx->value)); + if (!(c = (i_cmp((&r), rkey)))) + { res->ref = &tx->value; return tn; } + dir = (c < 0); + tx = tx->link[dir]; + } + tn = c_alloc(_cx_node); + tn->link[0] = tn->link[1] = (_cx_node*)&_csmap_sentinel; + tn->level = 1; + res->ref = &tn->value, res->inserted = true; + if (top == 0) + return tn; + up[top - 1]->link[dir] = tn; + while (top--) { + if (top) + dir = (up[top - 1]->link[1] == up[top]); + up[top] = _cx_memb(_skew_)(up[top]); + up[top] = _cx_memb(_split_)(up[top]); + if (top) + up[top - 1]->link[dir] = up[top]; + } + return up[0]; +} + +STC_DEF _cx_result +_cx_memb(_insert_entry_)(_cx_self* self, i_keyraw rkey) { + _cx_result res = {NULL}; + self->root = _cx_memb(_insert_entry_i_)(self->root, &rkey, &res); + self->size += res.inserted; + return res; +} + +static _cx_node* +_cx_memb(_erase_r_)(_cx_node *tn, const _cx_rawkey* rkey, int *erased) { + if (tn->level == 0) + return tn; + _cx_rawkey raw = i_keyto(_i_keyref(&tn->value)); + _cx_node *tx; int c = (i_cmp((&raw), rkey)); + if (c != 0) + tn->link[c < 0] = _cx_memb(_erase_r_)(tn->link[c < 0], rkey, erased); + else { + if (!*erased) + { _cx_memb(_value_drop)(&tn->value); *erased = 1; } + if (tn->link[0]->level && tn->link[1]->level) { + tx = tn->link[0]; + while (tx->link[1]->level) + tx = tx->link[1]; + tn->value = tx->value; + raw = i_keyto(_i_keyref(&tn->value)); + tn->link[0] = _cx_memb(_erase_r_)(tn->link[0], &raw, erased); + } else { /* unlink node */ + tx = tn; + tn = tn->link[tn->link[0]->level == 0]; + c_free(tx); + } + } + if (tn->link[0]->level < tn->level - 1 || tn->link[1]->level < tn->level - 1) { + if (tn->link[1]->level > --tn->level) + tn->link[1]->level = tn->level; + tn = _cx_memb(_skew_)(tn); + tx = tn->link[0] = _cx_memb(_skew_)(tn->link[0]); + tx->link[0] = _cx_memb(_skew_)(tx->link[0]); + tn = _cx_memb(_split_)(tn); + tn->link[0] = _cx_memb(_split_)(tn->link[0]); + } + return tn; +} + +STC_DEF int +_cx_memb(_erase)(_cx_self* self, i_keyraw rkey) { + int erased = 0; + self->root = _cx_memb(_erase_r_)(self->root, &rkey, &erased); + self->size -= erased; + return erased; +} + +STC_DEF _cx_iter +_cx_memb(_erase_at)(_cx_self* self, _cx_iter it) { + _cx_rawkey raw = i_keyto(_i_keyref(it.ref)), nxt; + _cx_memb(_next)(&it); + if (it.ref) + nxt = i_keyto(_i_keyref(it.ref)); + _cx_memb(_erase)(self, raw); + if (it.ref) + _cx_memb(_find_it)(self, nxt, &it); + return it; +} + +STC_DEF _cx_iter +_cx_memb(_erase_range)(_cx_self* self, _cx_iter it1, _cx_iter it2) { + if (!it2.ref) { + while (it1.ref) + it1 = _cx_memb(_erase_at)(self, it1); + return it1; + } + _cx_key k1 = *_i_keyref(it1.ref), k2 = *_i_keyref(it2.ref); + _cx_rawkey r1 = i_keyto((&k1)); + for (;;) { + if (memcmp(&k1, &k2, sizeof k1) == 0) + return it1; + _cx_memb(_next)(&it1); + k1 = *_i_keyref(it1.ref); + _cx_memb(_erase)(self, r1); + r1 = i_keyto((&k1)); + _cx_memb(_find_it)(self, r1, &it1); + } +} + +#if !defined i_no_clone +static _cx_node* +_cx_memb(_clone_r_)(_cx_node *tn) { + if (! tn->level) + return tn; + _cx_node *cn = c_alloc(_cx_node); + cn->level = tn->level; + cn->value = _cx_memb(_value_clone)(tn->value); + cn->link[0] = _cx_memb(_clone_r_)(tn->link[0]); + cn->link[1] = _cx_memb(_clone_r_)(tn->link[1]); + return cn; +} + +STC_DEF _cx_self +_cx_memb(_clone)(_cx_self cx) { + return c_INIT(_cx_self){_cx_memb(_clone_r_)(cx.root), cx.size}; +} +#endif // !i_no_clone + +#if !defined i_no_emplace +STC_DEF _cx_result +_cx_memb(_emplace)(_cx_self* self, i_keyraw rkey _i_MAP_ONLY(, i_valraw rmapped)) { + _cx_result res = _cx_memb(_insert_entry_)(self, rkey); + if (res.inserted) { + *_i_keyref(res.ref) = i_keyfrom(rkey); + _i_MAP_ONLY(res.ref->second = i_valfrom(rmapped);) + } + return res; +} +#endif // i_no_emplace + +static void +_cx_memb(_drop_r_)(_cx_node* tn) { + if (tn->level != 0) { + _cx_memb(_drop_r_)(tn->link[0]); + _cx_memb(_drop_r_)(tn->link[1]); + _cx_memb(_value_drop)(&tn->value); + c_free(tn); + } +} + +STC_DEF void +_cx_memb(_drop)(_cx_self* self) { + _cx_memb(_drop_r_)(self->root); +} + +#endif // i_implement +#undef _i_isset +#undef _i_ismap +#undef _i_keyref +#undef _i_MAP_ONLY +#undef _i_SET_ONLY +#define CSMAP_H_INCLUDED +#include <stc/priv/template.h> diff --git a/misc/include/alt/cstr.h b/misc/include/alt/cstr.h new file mode 100644 index 00000000..ecb9c9fb --- /dev/null +++ b/misc/include/alt/cstr.h @@ -0,0 +1,385 @@ +/* MIT License + * + * Copyright (c) 2022 Tyge Løvset, NORCE, www.norceresearch.no + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef CSTR_H_INCLUDED +#define CSTR_H_INCLUDED + +#include <stc/ccommon.h> +#include <stc/forward.h> +#include <stdlib.h> /* malloc */ +#include <string.h> +#include <stdarg.h> +#include <stdio.h> /* vsnprintf */ +#include <ctype.h> + +#define c_unchecked_container_of(ptr, type, member) \ + ((type*)((char*)(ptr) - offsetof(type, member))) + +typedef char cstr_value; +typedef struct { cstr_value* str; } cstr; +typedef struct { size_t size, cap; char chr[1]; } cstr_priv; +#define _cstr_p(self) c_unchecked_container_of((self)->str, cstr_priv, chr) +#ifdef i_static + static cstr_priv _cstr_nullrep = {0, 0, {0}}; + static const cstr cstr_NULL = {_cstr_nullrep.chr}; +#else + extern const cstr cstr_NULL; +#endif +/* optimal memory: based on malloc_usable_size() sequence: 24, 40, 56, ... */ +#define _cstr_opt_mem(cap) ((((offsetof(cstr_priv, chr) + (cap) + 8)>>4)<<4) + 8) +/* optimal string capacity: 7, 23, 39, ... */ +#define _cstr_opt_cap(cap) (_cstr_opt_mem(cap) - offsetof(cstr_priv, chr) - 1) + +STC_API cstr cstr_from_n(const char* str, size_t n); +STC_API cstr cstr_from_fmt(const char* fmt, ...); +STC_API char* cstr_reserve(cstr* self, size_t cap); +STC_API void cstr_resize(cstr* self, size_t len, char fill); +STC_API cstr* cstr_assign_n(cstr* self, const char* str, size_t n); +STC_API int cstr_printf(cstr* self, const char* fmt, ...); +STC_API cstr* cstr_append_n(cstr* self, const char* str, size_t n); +STC_API cstr cstr_replace_sv(csview str, csview find, csview repl, unsigned count); +STC_DEF void cstr_replace_at_sv(cstr* self, const size_t pos, size_t len, csview repl); +STC_API void cstr_erase(cstr* self, size_t pos, size_t n); +STC_API size_t cstr_find(const cstr* self, const char* needle); +STC_API size_t cstr_find_at(const cstr* self, size_t pos, const char* needle); +STC_API bool cstr_getdelim(cstr *self, int delim, FILE *stream); + +STC_INLINE cstr cstr_init() { return cstr_NULL; } +STC_INLINE const char* cstr_str(const cstr* self) { return self->str; } +#define cstr_toraw(self) (self)->str +STC_INLINE csview cstr_sv(const cstr* self) + { return c_INIT(csview){self->str, _cstr_p(self)->size}; } +#define cstr_lit(literal) \ + cstr_from_n(literal, c_strlen_lit(literal)) +STC_INLINE cstr cstr_from(const char* str) + { return cstr_from_n(str, strlen(str)); } +STC_INLINE char* cstr_data(cstr* self) { return self->str; } +STC_INLINE size_t cstr_size(const cstr* self) { return _cstr_p(self)->size; } +STC_INLINE size_t cstr_capacity(cstr s) { return _cstr_p(&s)->cap; } +STC_INLINE bool cstr_empty(cstr s) { return _cstr_p(&s)->size == 0; } +STC_INLINE void cstr_drop(cstr* self) + { if (_cstr_p(self)->cap) c_free(_cstr_p(self)); } +STC_INLINE cstr cstr_clone(cstr s) + { return cstr_from_n(s.str, _cstr_p(&s)->size); } +STC_INLINE void cstr_clear(cstr* self) + { self->str[_cstr_p(self)->size = 0] = '\0'; } +STC_INLINE cstr* cstr_assign(cstr* self, const char* str) + { return cstr_assign_n(self, str, strlen(str)); } +STC_INLINE cstr* cstr_copy(cstr* self, cstr s) + { return cstr_assign_n(self, s.str, _cstr_p(&s)->size); } +STC_INLINE cstr* cstr_append(cstr* self, const char* str) + { return cstr_append_n(self, str, strlen(str)); } +STC_INLINE cstr* cstr_append_s(cstr* self, cstr s) + { return cstr_append_n(self, s.str, _cstr_p(&s)->size); } +STC_INLINE void cstr_push_back(cstr* self, char value) + { cstr_append_n(self, &value, 1); } +STC_INLINE void cstr_pop_back(cstr* self) + { self->str[ --_cstr_p(self)->size ] = '\0'; } +STC_INLINE void cstr_insert_n(cstr* self, const size_t pos, const char* str, const size_t n) + { cstr_replace_at_sv(self, pos, 0, c_SV(str, n)); } +STC_INLINE void cstr_insert(cstr* self, const size_t pos, const char* str) + { cstr_replace_at_sv(self, pos, 0, c_SV(str, strlen(str))); } +STC_INLINE void cstr_insert_s(cstr* self, const size_t pos, cstr s) + { cstr_replace_at_sv(self, pos, 0, c_SV(s.str, _cstr_p(&s)->size)); } +STC_INLINE void cstr_replace_at(cstr* self, const size_t pos, const size_t len, const char* str) + { cstr_replace_at_sv(self, pos, len, c_SV(str, strlen(str))); } +STC_INLINE void cstr_replace_s(cstr* self, const size_t pos, const size_t len, cstr s) + { cstr_replace_at_sv(self, pos, len, c_SV(s.str, _cstr_p(&s)->size)); } +STC_INLINE char* cstr_front(cstr* self) { return self->str; } +STC_INLINE char* cstr_back(cstr* self) + { return self->str + _cstr_p(self)->size - 1; } +STC_INLINE bool cstr_equals(const cstr* self, const char* str) + { return strcmp(self->str, str) == 0; } +STC_INLINE bool cstr_equals_s(const cstr* self, cstr s) + { return strcmp(self->str, s.str) == 0; } +STC_INLINE bool cstr_contains(const cstr* self, const char* needle) + { return strstr(self->str, needle) != NULL; } +STC_INLINE bool cstr_getline(cstr *self, FILE *stream) + { return cstr_getdelim(self, '\n', stream); } + +STC_INLINE cstr_buf cstr_buffer(cstr* s) { + cstr_priv* p = _cstr_p(s); + return c_INIT(cstr_buf){s->str, p->size, p->cap}; +} + +STC_INLINE cstr cstr_with_capacity(const size_t cap) { + cstr s = cstr_NULL; + cstr_reserve(&s, cap); + return s; +} + +STC_INLINE cstr cstr_with_size(const size_t len, const char fill) { + cstr s = cstr_NULL; + cstr_resize(&s, len, fill); + return s; +} + +STC_INLINE char* cstr_append_uninit(cstr *self, size_t n) { + size_t len = cstr_size(self); char* d; + if (!(d = cstr_reserve(self, len + n))) return NULL; + _cstr_p(self)->size += n; + return d + len; +} + +STC_INLINE cstr* cstr_take(cstr* self, cstr s) { + if (self->str != s.str && _cstr_p(self)->cap) + c_free(_cstr_p(self)); + self->str = s.str; + return self; +} + +STC_INLINE cstr cstr_move(cstr* self) { + cstr tmp = *self; + *self = cstr_NULL; + return tmp; +} + +STC_INLINE bool cstr_starts_with(const cstr* self, const char* sub) { + const char* p = self->str; + while (*sub && *p == *sub) ++p, ++sub; + return *sub == 0; +} + +STC_INLINE bool cstr_ends_with(const cstr* self, const char* sub) { + const size_t n = strlen(sub), sz = _cstr_p(self)->size; + return n <= sz && !memcmp(self->str + sz - n, sub, n); +} + +STC_INLINE int c_strncasecmp(const char* s1, const char* s2, size_t nmax) { + int ret = 0; + while (nmax-- && (ret = tolower(*s1++) - tolower(*s2)) == 0 && *s2++) + ; + return ret; +} + +/* container adaptor functions: */ +#define cstr_cmp(xp, yp) strcmp((xp)->str, (yp)->str) + +STC_INLINE bool cstr_eq(const cstr* x, const cstr* y) { + size_t xs = _cstr_p(x)->size, ys = _cstr_p(y)->size; + return xs == ys && !memcmp(x->str, y->str, xs); +} +STC_INLINE uint64_t cstr_hash(const cstr *self) { + return cfasthash(self->str, _cstr_p(self)->size); +} + +STC_INLINE void +cstr_replace(cstr* self, const char* find, const char* repl, unsigned count) { + csview in = cstr_sv(self); + cstr_take(self, cstr_replace_sv(in, c_SV(find, strlen(find)), + c_SV(repl, strlen(repl)), count)); +} + +/* -------------------------- IMPLEMENTATION ------------------------- */ +#if defined(i_implement) + +#ifndef i_static +static cstr_priv _cstr_nullrep = {0, 0, {0}}; +const cstr cstr_NULL = {_cstr_nullrep.chr}; +#endif + +STC_DEF char* +cstr_reserve(cstr* self, const size_t cap) { + cstr_priv *p = _cstr_p(self); + const size_t oldcap = p->cap; + if (cap > oldcap) { + p = (cstr_priv*) c_realloc(((oldcap != 0) & (p != &_cstr_nullrep)) ? p : NULL, _cstr_opt_mem(cap)); + if (!p) return NULL; + self->str = p->chr; + if (oldcap == 0) self->str[p->size = 0] = '\0'; + p->cap = _cstr_opt_cap(cap); + } + return self->str; +} + +STC_DEF void +cstr_resize(cstr* self, const size_t len, const char fill) { + const size_t n = _cstr_p(self)->size; + cstr_reserve(self, len); + if (len > n) memset(self->str + n, fill, len - n); + if (len | n) self->str[_cstr_p(self)->size = len] = '\0'; +} + +STC_DEF cstr +cstr_from_n(const char* str, const size_t n) { + if (n == 0) return cstr_NULL; + cstr_priv* prv = (cstr_priv*) c_malloc(_cstr_opt_mem(n)); + cstr s = {(char *) memcpy(prv->chr, str, n)}; + s.str[prv->size = n] = '\0'; + prv->cap = _cstr_opt_cap(n); + return s; +} + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdeprecated-declarations" +#elif defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4996) +#endif + +STC_DEF int +cstr_vfmt(cstr* self, const char* fmt, va_list args) { + va_list args2; + va_copy(args2, args); + int len = vsnprintf(NULL, (size_t)0, fmt, args); + cstr_reserve(self, len); + vsprintf(self->str, fmt, args2); + va_end(args2); + return _cstr_p(self)->size = len; +} + +#if defined(__clang__) +# pragma clang diagnostic pop +#elif defined(_MSC_VER) +# pragma warning(pop) +#endif + +STC_DEF cstr +cstr_from_fmt(const char* fmt, ...) { + cstr ret = cstr_NULL; + va_list args; va_start(args, fmt); + cstr_vfmt(&ret, fmt, args); + va_end(args); + return ret; +} + +STC_DEF int +cstr_printf(cstr* self, const char* fmt, ...) { + cstr ret = cstr_NULL; + va_list args; + va_start(args, fmt); + int n = cstr_vfmt(&ret, fmt, args); + va_end(args); + cstr_drop(self); + *self = ret; + return n; +} + +STC_DEF cstr* +cstr_assign_n(cstr* self, const char* str, const size_t n) { + if (n || _cstr_p(self)->cap) { + cstr_reserve(self, n); + memmove(self->str, str, n); + self->str[_cstr_p(self)->size = n] = '\0'; + } + return self; +} + +STC_DEF cstr* +cstr_append_n(cstr* self, const char* str, const size_t n) { + if (n == 0) return self; + const size_t oldlen = _cstr_p(self)->size, newlen = oldlen + n; + if (newlen > _cstr_p(self)->cap) { + const size_t off = (size_t) (str - self->str); /* handle self append */ + cstr_reserve(self, (oldlen*3 >> 1) + n); + if (off <= oldlen) str = self->str + off; + } + memcpy(&self->str[oldlen], str, n); + self->str[_cstr_p(self)->size = newlen] = '\0'; + return self; +} + +STC_INLINE void _cstr_internal_move(cstr* self, const size_t pos1, const size_t pos2) { + if (pos1 == pos2) + return; + const size_t len = _cstr_p(self)->size, newlen = len + pos2 - pos1; + if (newlen > _cstr_p(self)->cap) + cstr_reserve(self, (len*3 >> 1) + pos2 - pos1); + memmove(&self->str[pos2], &self->str[pos1], len - pos1); + self->str[_cstr_p(self)->size = newlen] = '\0'; +} + +STC_DEF void +cstr_replace_at_sv(cstr* self, const size_t pos, size_t len, csview repl) { + const size_t sz = cstr_size(self); + if (len > sz - pos) len = sz - pos; + char buf[256], *xstr = repl.size > 256 ? c_malloc(repl.size) : buf; + memcpy(xstr, repl.str, repl.size); + _cstr_internal_move(self, pos + len, pos + repl.size); + memcpy(&self->str[pos], xstr, repl.size); + if (repl.size > 256) c_free(xstr); +} + +STC_DEF cstr +cstr_replace_sv(csview str, csview find, csview repl, unsigned count) { + cstr out = cstr_NULL; + size_t from = 0; char* res; + if (count == 0) count = ~0; + if (find.size) + while (count-- && (res = cstrnstrn(str.str + from, find.str, str.size - from, find.size))) { + const size_t pos = res - str.str; + cstr_append_n(&out, str.str + from, pos - from); + cstr_append_n(&out, repl.str, repl.size); + from = pos + find.size; + } + cstr_append_n(&out, str.str + from, str.size - from); + return out; +} + +STC_DEF void +cstr_erase(cstr* self, const size_t pos, size_t n) { + const size_t len = _cstr_p(self)->size; + if (n > len - pos) n = len - pos; + if (len) { + memmove(&self->str[pos], &self->str[pos + n], len - (pos + n)); + self->str[_cstr_p(self)->size -= n] = '\0'; + } +} + +STC_DEF bool +cstr_getdelim(cstr *self, const int delim, FILE *fp) { + size_t pos = 0, cap = _cstr_p(self)->cap; + char* d = self->str; + int c = fgetc(fp); + if (c == EOF) + return false; + for (;;) { + if (c == delim || c == EOF) { + if (cap) d[_cstr_p(self)->size = pos] = '\0'; + return true; + } + if (pos == cap) { + d = cstr_reserve(self, (cap*3 >> 1) + 16); + cap = cstr_capacity(*self); + } + d[pos++] = (char) c; + c = fgetc(fp); + } +} + +STC_DEF size_t +cstr_find(const cstr* self, const char* needle) { + char* res = strstr(self->str, needle); + return res ? res - self->str : c_NPOS; +} + +STC_DEF size_t +cstr_find_at(const cstr* self, const size_t pos, const char* needle) { + if (pos > _cstr_p(self)->size) return c_NPOS; + char* res = strstr(self->str + pos, needle); + return res ? res - self->str : c_NPOS; +} + +#endif +#endif // CSTR_H_INCLUDED +#undef i_opt diff --git a/misc/include/fmt.h b/misc/include/fmt.h new file mode 100644 index 00000000..df1fe990 --- /dev/null +++ b/misc/include/fmt.h @@ -0,0 +1,253 @@ +#ifndef FMT_INCLUDED
+#define FMT_INCLUDED
+/*
+void fmt_print(dst, fmt, ...);
+void fmt_destroy(fmt_buffer* buf);
+
+ dst - destination
+ int 1=stdout, 2=stderr
+ FILE* file Write to a file
+ char* strbuf Write to a pre-allocated string buffer
+ fmt_buffer* b Auto realloc the needed memory. Set b->stream=1 for stream-mode.
+ b->data must be freed after usage.
+
+ fmt - format string
+ {} Auto-detected format. If :MOD is not specified,
+ float will use ".8g" format, and double ".16g".
+ {:MOD} Format modifiers: < left align (replaces -), default for char*, char.
+ > right align, default for numbers.
+ Other than that MOD can be normal printf format modifiers.
+ {{, }} Print chars {, and }. (note: a single % prints %).
+
+* C11 or higher required.
+* MAX 255 chars fmt string by default. MAX 12 arguments after fmt string.
+* Static linking by default, shared symbols by defining FMT_HEADER / FMT_IMPLEMENT.
+* (c) operamint, 2022, MIT License.
+-----------------------------------------------------------------------------------
+#include "fmt.h"
+
+int main() {
+ const double pi = 3.141592653589793;
+ const size_t x = 1234567890;
+ const char* string = "Hello world";
+ const char z = 'z';
+ _Bool flag = 1;
+ unsigned char r = 123, g = 214, b = 90, w = 110;
+ char buffer[64];
+
+ fmt_print(1, "Color: ({} {} {}), {}\n", r, g, b, flag);
+ fmt_print(1, "{:10} {:10} {:10.2f}\n", 42ull, 43, pi);
+ fmt_print(stdout, "{:>10} {:>10} {:>10}\n", z, z, w);
+ fmt_print(stdout, "{:10} {:10} {:10}\n", "Hello", "Mad", "World");
+ fmt_print(stderr, "100%: {:<20} {:.*} {}\n", string, 4, pi, x);
+ fmt_print(buffer, "Precision: {} {:.10} {}", string, pi, x);
+ fmt_print(1, "{}\n", buffer);
+ fmt_print(1, "Vector: ({}, {}, {})\n", 3.2, 3.3, pi);
+
+ fmt_buffer out[1] = {{.stream=1}};
+ fmt_print(out, "{} {}", "Pi is:", pi);
+ fmt_print(1, "{}, len={}, cap={}\n", out->data, out->len, out->cap);
+ fmt_print(out, "{} {}", ", Pi squared is:", pi*pi);
+ fmt_print(1, "{}, len={}, cap={}\n", out->data, out->len, out->cap);
+ fmt_destroy(out);
+}
+*/
+#include <stdio.h>
+#include <assert.h>
+
+#define fmt_OVERLOAD(name, ...) \
+ fmt_JOIN(name, fmt_NUMARGS(__VA_ARGS__))(__VA_ARGS__)
+#define fmt_CONCAT(a, b) a ## b
+#define fmt_JOIN(a, b) fmt_CONCAT(a, b)
+#define fmt_EXPAND(...) __VA_ARGS__
+#define fmt_NUMARGS(...) _fmt_APPLY_ARG_N((__VA_ARGS__, _fmt_RSEQ_N))
+
+#define _fmt_APPLY_ARG_N(args) fmt_EXPAND(_fmt_ARG_N args)
+#define _fmt_RSEQ_N 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
+#define _fmt_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, \
+ _14, _15, _16, N, ...) N
+
+#if defined FMT_HEADER || defined FMT_IMPLEMENT
+# define FMT_API
+#else
+# define FMT_API static inline
+#endif
+
+typedef struct {
+ char* data;
+ size_t cap, len;
+ _Bool stream;
+} fmt_buffer;
+
+FMT_API void fmt_destroy(fmt_buffer* buf);
+FMT_API int _fmt_parse(char* p, int nargs, const char *fmt, ...);
+FMT_API void _fmt_iprint(int fd, const char* fmt, ...);
+FMT_API void _fmt_bprint(fmt_buffer*, const char* fmt, ...);
+
+#ifndef FMT_MAX
+#define FMT_MAX 256
+#endif
+
+/* Primary function. */
+#define fmt_print(...) fmt_OVERLOAD(fmt_print, __VA_ARGS__)
+#define fmt_print2(to, fmt) \
+ do { char _fs[FMT_MAX]; int _n = _fmt_parse(_fs, 0, fmt); \
+ assert(_n == 0); _fmt_fn(to)(to, fmt); } while (0)
+#define fmt_print3(to, fmt, c) \
+ do { char _fs[FMT_MAX]; int _n = _fmt_parse(_fs, 1, fmt, _fc(c)); \
+ assert(_n == 1); _fmt_fn(to)(to, _fs, c); } while (0)
+#define fmt_print4(to, fmt, c, d) \
+ do { char _fs[FMT_MAX]; int _n = _fmt_parse(_fs, 2, fmt, _fc(c), _fc(d)); \
+ assert(_n == 2); _fmt_fn(to)(to, _fs, c, d); } while (0)
+#define fmt_print5(to, fmt, c, d, e) \
+ do { char _fs[FMT_MAX]; int _n = _fmt_parse(_fs, 3, fmt, _fc(c), _fc(d), _fc(e)); \
+ assert(_n == 3); _fmt_fn(to)(to, _fs, c, d, e); } while (0)
+#define fmt_print6(to, fmt, c, d, e, f) \
+ do { char _fs[FMT_MAX]; int _n = _fmt_parse(_fs, 4, fmt, _fc(c), _fc(d), _fc(e), _fc(f)); \
+ assert(_n == 4); _fmt_fn(to)(to, _fs, c, d, e, f); } while (0)
+#define fmt_print7(to, fmt, c, d, e, f, g) \
+ do { char _fs[FMT_MAX]; int _n = _fmt_parse(_fs, 5, fmt, _fc(c), _fc(d), _fc(e), _fc(f), _fc(g)); \
+ assert(_n == 5); _fmt_fn(to)(to, _fs, c, d, e, f, g); } while (0)
+#define fmt_print8(to, fmt, c, d, e, f, g, h) \
+ do { char _fs[FMT_MAX]; int _n = _fmt_parse(_fs, 6, fmt, _fc(c), _fc(d), _fc(e), _fc(f), _fc(g), _fc(h)); \
+ assert(_n == 6); _fmt_fn(to)(to, _fs, c, d, e, f, g, h); } while (0)
+#define fmt_print9(to, fmt, c, d, e, f, g, h, i) \
+ do { char _fs[FMT_MAX]; int _n = _fmt_parse(_fs, 7, fmt, _fc(c), _fc(d), _fc(e), _fc(f), _fc(g), _fc(h), _fc(i)); \
+ assert(_n == 7); _fmt_fn(to)(to, _fs, c, d, e, f, g, h, i); } while (0)
+#define fmt_print10(to, fmt, c, d, e, f, g, h, i, j) \
+ do { char _fs[FMT_MAX]; int _n = _fmt_parse(_fs, 8, fmt, _fc(c), _fc(d), _fc(e), _fc(f), _fc(g), _fc(h), \
+ _fc(i), _fc(j)); \
+ assert(_n == 8); _fmt_fn(to)(to, _fs, c, d, e, f, g, h, i, j); } while (0)
+#define fmt_print11(to, fmt, c, d, e, f, g, h, i, j, k) \
+ do { char _fs[FMT_MAX]; int _n = _fmt_parse(_fs, 9, fmt, _fc(c), _fc(d), _fc(e), _fc(f), _fc(g), _fc(h), \
+ _fc(i), _fc(j), _fc(k)); \
+ assert(_n == 9); _fmt_fn(to)(to, _fs, c, d, e, f, g, h, i, j, k); } while (0)
+#define fmt_print12(to, fmt, c, d, e, f, g, h, i, j, k, m) \
+ do { char _fs[FMT_MAX]; int _n = _fmt_parse(_fs, 10, fmt, _fc(c), _fc(d), _fc(e), _fc(f), _fc(g), _fc(h), \
+ _fc(i), _fc(j), _fc(k), _fc(m)); \
+ assert(_n == 10); _fmt_fn(to)(to, _fs, c, d, e, f, g, h, i, j, k, m); } while (0)
+#define fmt_print13(to, fmt, c, d, e, f, g, h, i, j, k, m, n) \
+ do { char _fs[FMT_MAX]; int _n = _fmt_parse(_fs, 11, fmt, _fc(c), _fc(d), _fc(e), _fc(f), _fc(g), _fc(h), \
+ _fc(i), _fc(j), _fc(k), _fc(m), _fc(n)); \
+ assert(_n == 11); _fmt_fn(to)(to, _fs, c, d, e, f, g, h, i, j, k, m, n); } while (0)
+#define fmt_print14(to, fmt, c, d, e, f, g, h, i, j, k, m, n, o) \
+ do { char _fs[FMT_MAX]; int _n = _fmt_parse(_fs, 12, fmt, _fc(c), _fc(d), _fc(e), _fc(f), _fc(g), _fc(h), \
+ _fc(i), _fc(j), _fc(k), _fc(m), _fc(n), _fc(o)); \
+ assert(_n == 12); _fmt_fn(to)(to, _fs, c, d, e, f, g, h, i, j, k, m, n, o); } while (0)
+
+#define _fmt_fn(x) _Generic ((x), \
+ FILE*: fprintf, \
+ char*: sprintf, \
+ int: _fmt_iprint, \
+ fmt_buffer*: _fmt_bprint)
+
+#if defined(_MSC_VER) && !defined(__clang__)
+# define _signed_char_hhd
+#else
+# define _signed_char_hhd signed char: "hhd",
+#endif
+
+#define _fc(x) _Generic (x, \
+ _Bool: "d", \
+ unsigned char: "hhu", \
+ _signed_char_hhd \
+ char: "c", \
+ short: "hd", \
+ unsigned short: "hu", \
+ int: "d", \
+ unsigned: "u", \
+ long: "ld", \
+ unsigned long: "lu", \
+ long long: "lld", \
+ unsigned long long: "llu", \
+ float: "g", \
+ double: "@g", \
+ long double: "@Lg", \
+ char*: "s", \
+ void*: "p", \
+ const char*: "s", \
+ const void*: "p")
+
+#if defined FMT_IMPLEMENT || !(defined FMT_HEADER || defined FMT_IMPLEMENT)
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+FMT_API void fmt_destroy(fmt_buffer* buf) {
+ free(buf->data);
+}
+
+FMT_API void _fmt_iprint(int fd, const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(fd == 1 ? stdout : stderr, fmt, args);
+ va_end(args);
+}
+
+FMT_API void _fmt_bprint(fmt_buffer* buf, const char* fmt, ...) {
+ va_list args, args2;
+ va_start(args, fmt);
+ va_copy(args2, args);
+ const int n = vsnprintf(NULL, 0U, fmt, args);
+ const size_t pos = buf->stream ? buf->len : 0U;
+ buf->len = pos + (size_t)n;
+ if (buf->len > buf->cap) {
+ buf->cap = buf->len + buf->cap/2;
+ buf->data = (char*)realloc(buf->data, buf->cap + 1);
+ }
+ vsprintf(buf->data + pos, fmt, args2);
+ va_end(args2);
+ va_end(args);
+}
+
+FMT_API int _fmt_parse(char* p, int nargs, const char *fmt, ...) {
+ char *arg, *p0, ch;
+ int n = 0, empty;
+ va_list args;
+ va_start(args, fmt);
+ do {
+ switch ((ch = *fmt)) {
+ case '%':
+ *p++ = '%';
+ break;
+ case '}':
+ if (*++fmt == '}') break; /* ok */
+ n = 99;
+ continue;
+ case '{':
+ if (*++fmt == '{') break; /* ok */
+ if (++n > nargs) continue;
+ if (*fmt != ':' && *fmt != '}') n = 99;
+ fmt += (*fmt == ':');
+ empty = *fmt == '}';
+ arg = va_arg(args, char *);
+ *p++ = '%', p0 = p;
+ while (1) switch (*fmt) {
+ case '\0': n = 99; /* nobreak */
+ case '}': goto done;
+ case '<': *p++ = '-', ++fmt; break;
+ case '>': p0 = NULL; /* nobreak */
+ case '-': ++fmt; break;
+ case '*': if (++n <= nargs) arg = va_arg(args, char *); /* nobreak */
+ default: *p++ = *fmt++;
+ }
+ done:
+ switch (*arg) {
+ case 'g': if (empty) memcpy(p, ".8", 2), p += 2; break;
+ case '@': ++arg; if (empty) memcpy(p, ".16", 3), p += 3; break;
+ }
+ if (!strchr("csdioxXufFeEaAgGnp", fmt[-1]))
+ while (*arg) *p++ = *arg++;
+ if (p0 && (p[-1] == 's' || p[-1] == 'c')) /* left-align str */
+ memmove(p0 + 1, p0, p++ - p0), *p0 = '-';
+ fmt += *fmt == '}';
+ continue;
+ }
+ *p++ = *fmt++;
+ } while (ch);
+ va_end(args);
+ return n;
+}
+#endif
+#endif
\ No newline at end of file diff --git a/misc/include/stctest.h b/misc/include/stctest.h new file mode 100644 index 00000000..9718af7f --- /dev/null +++ b/misc/include/stctest.h @@ -0,0 +1,203 @@ +/* MIT License
+ *
+ * Copyright (c) 2022 Tyge Løvset, NORCE, www.norceresearch.no
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/* stctest: A small and simple C11 unit-testing framework.
+
+ Features:
+ - Requires C11. Should work with any C compiler supporting _Generic.
+ - No library dependencies. Not even itself. Just a header file.
+ - Reports assertion failures, including expressions and line numbers.
+ - ANSI color output for maximum visibility.
+ - Easy to embed in apps for runtime tests (e.g. environment tests).
+
+ Example:
+
+ #include "stctest.h"
+ #include "mylib.h"
+
+ void test_sheep()
+ {
+ EXPECT_EQ("Sheep are cool", are_sheep_cool());
+ EXPECT_EQ(4, sheep.legs);
+ }
+
+ void test_cheese()
+ {
+ EXPECT_GT(cheese.tanginess, 0);
+ EXPECT_EQ("Wensleydale", cheese.name);
+ }
+
+ int main()
+ {
+ RUN_TEST(test_sheep);
+ RUN_TEST(test_cheese);
+ return REPORT_TESTS();
+ }
+ */
+
+#ifndef STCTEST_INCLUDED
+#define STCTEST_INCLUDED
+
+#include <stdio.h>
+#include <time.h>
+#include <string.h>
+#include <float.h>
+#include <stdarg.h>
+#include <inttypes.h>
+
+#define STC_FLOAT_EPSILON 1e-6
+#define STC_DOUBLE_EPSILON 1e-13
+
+
+#define EXPECT_TRUE(expr) \
+ do { if (!_stctest_assert(__FILE__, __LINE__, #expr, (expr) != 0)) puts(""); } while (0)
+
+#define EXPECT_TRUE1(expr, v) \
+ do { if (!_stctest_assert(__FILE__, __LINE__, #expr, (expr) != 0)) { \
+ char _fmt[32]; sprintf(_fmt, " ; %%s --> %s\n", _stctest_FMT(v)); \
+ printf(_fmt, #v, v); \
+ }} while (0)
+
+#define EXPECT_FALSE(expr) EXPECT_TRUE(!(expr))
+#define EXPECT_FALSE1(expr, v) EXPECT_TRUE1(!(expr), v)
+
+/* NB! (char*) are compared as strings. Cast to (void*) to compare pointers only */
+#define EXPECT_EQ(a, b) _stctest_CHECK(a, ==, b, -STC_DOUBLE_EPSILON)
+#define EXPECT_NE(a, b) _stctest_CHECK(a, !=, b, -STC_DOUBLE_EPSILON)
+#define EXPECT_GT(a, b) _stctest_CHECK(a, >, b, -STC_DOUBLE_EPSILON)
+#define EXPECT_LT(a, b) _stctest_CHECK(a, <, b, -STC_DOUBLE_EPSILON)
+#define EXPECT_LE(a, b) _stctest_CHECK(a, <=, b, -STC_DOUBLE_EPSILON)
+#define EXPECT_GE(a, b) _stctest_CHECK(a, >=, b, -STC_DOUBLE_EPSILON)
+#define EXPECT_FLOAT_EQ(a, b) _stctest_CHECK((float)(a), ==, (float)(b), -STC_FLOAT_EPSILON)
+#define EXPECT_DOUBLE_EQ(a, b) _stctest_CHECK((double)(a), ==, (double)(b), -STC_DOUBLE_EPSILON)
+#define EXPECT_NEAR(a, b, abs_error) _stctest_CHECK((double)(a), ==, (double)(b), abs_error)
+
+/* Run a test() function */
+#define RUN_TEST(test, ...) do { \
+ puts(#test "(" #__VA_ARGS__ "):"); \
+ const int ps = _stctest_s.passes; \
+ const int fs = _stctest_s.fails; \
+ const clock_t _start = clock(); \
+ test(__VA_ARGS__); \
+ const int _sum = (clock() - _start)*1000 / CLOCKS_PER_SEC; \
+ _stctest_s.total_ms += _sum; \
+ printf(" passed: %d/%d. duration: %d ms\n", \
+ _stctest_s.passes - ps, _stctest_s.passes + _stctest_s.fails - (ps + fs), _sum); \
+} while (0)
+
+#define REPORT_TESTS() stctest_report()
+
+/* ----------------------------------------------------------------------------- */
+
+#define _stctest_CHECK(a, OP, b, e) \
+ do { if (!_stctest_assert(__FILE__, __LINE__, #a " " #OP " " #b, _stctest_CMP(a, OP, b, e))) { \
+ char _fmt[32]; sprintf(_fmt, " ; %s %s %s\n", _stctest_FMT(a), #OP, _stctest_FMT(b)); \
+ printf(_fmt, a, b); \
+ }} while (0)
+
+#define _stctest_CMP(a, OP, b, e) _Generic((a), \
+ const char*: _stctest_strcmp, char*: _stctest_strcmp, \
+ double: _Generic((b), double: _stctest_dblcmp, float: _stctest_dblcmp, default: _stctest_valcmp), \
+ float: _Generic((b), double: _stctest_dblcmp, float: _stctest_dblcmp, default: _stctest_valcmp), \
+ default: _stctest_valcmp)((a) OP (b), #OP, a, b, (double)(e))
+
+#define _stctest_FMT(a) _Generic((a), \
+ float: "%.8gf", double: "%.15g", \
+ int64_t: "%" PRId64, int32_t: "%" PRId32, int16_t: "%" PRId16, int8_t: "%" PRId8, \
+ uint64_t: "%" PRIu64 "u", uint32_t: "%" PRIu32 "u", uint16_t: "%" PRIu16 "u", uint8_t: "%" PRIu8 "u", \
+ char*: "`%s`", const char*: "`%s`", \
+ default: "%p")
+
+static int _stctest_strcmp(int res, const char* OP, ...) {
+ va_list ap;
+ va_start(ap, OP);
+ const char* a = va_arg(ap, const char *);
+ const char* b = va_arg(ap, const char *);
+ va_end(ap);
+ int c = strcmp(a, b);
+ if (OP[0] == '<') return OP[1] == '=' ? c <= 0 : c < 0;
+ if (OP[0] == '>') return OP[1] == '=' ? c >= 0 : c > 0;
+ return (OP[0] == '!') ^ (c == 0);
+}
+
+// Knuth:
+static int approximately_equal(double a, double b, double epsilon) {
+ double d = a - b; if (d < 0) d = -d;
+ if (a < 0) a = -a; if (b < 0) b = -b;
+ return d <= ((a < b ? b : a) * epsilon); // (a > b ? b : a) => essentially_equal:
+}
+
+static int _stctest_dblcmp(int res, const char* OP, ...) {
+ va_list ap;
+ va_start(ap, OP);
+ double a = va_arg(ap, double);
+ double b = va_arg(ap, double);
+ double e = va_arg(ap, double);
+ double c = a - b;
+ va_end(ap);
+ if (OP[0] == '<') return OP[1] == '=' ? c <= 0 : c < 0;
+ if (OP[0] == '>') return OP[1] == '=' ? c >= 0 : c > 0;
+ return (OP[0] == '!') ^ (e < 0 ? approximately_equal(a, b, -e) : (c < 0 ? -c : c) <= e);
+}
+
+static int _stctest_valcmp(int res, const char* OP, ...)
+ { return res; }
+
+#define _stctest_COLOR_CODE 0x1B
+#define _stctest_COLOR_RED "[1;31m"
+#define _stctest_COLOR_GREEN "[1;32m"
+#define _stctest_COLOR_RESET "[0m"
+
+static struct stctest_s {
+ int passes;
+ int fails;
+ const char* current_file;
+ int total_ms;
+} _stctest_s = {0};
+
+static int _stctest_assert(const char* file, int line, const char* expression, int pass) {
+ if (pass)
+ _stctest_s.passes++;
+ else {
+ _stctest_s.fails++;
+ printf(" failed \"%s:%d\": (%s)", file, line, expression);
+ }
+ _stctest_s.current_file = file;
+ return pass;
+}
+
+static int stctest_report(void) {
+ if (_stctest_s.fails) {
+ printf("%c%sFAILED%c%s: \"%s\": (failed %d, passed %d, total %d)\n",
+ _stctest_COLOR_CODE, _stctest_COLOR_RED, _stctest_COLOR_CODE, _stctest_COLOR_RESET,
+ _stctest_s.current_file, _stctest_s.fails, _stctest_s.passes, _stctest_s.passes + _stctest_s.fails);
+ } else {
+ printf("%c%sPASSED%c%s: \"%s\": (total %d)\n",
+ _stctest_COLOR_CODE, _stctest_COLOR_GREEN, _stctest_COLOR_CODE, _stctest_COLOR_RESET,
+ _stctest_s.current_file, _stctest_s.passes);
+ }
+ printf(" duration: %d ms\n", _stctest_s.total_ms); \
+ return -_stctest_s.fails;
+}
+
+#endif
diff --git a/misc/tests/cregex_test.c b/misc/tests/cregex_test.c new file mode 100644 index 00000000..b9642159 --- /dev/null +++ b/misc/tests/cregex_test.c @@ -0,0 +1,260 @@ +#if 0 +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <stc/csview.h> +#include <stc/stctest.h> +#include <stc/cregex.h> + +#define START_TEST(f) void f(void) +#define END_TEST + +#define M_START(re, m) ((m).str - (re).input) +#define M_END(re, m) (M_START(re, m) + (m).size) + +START_TEST(compile_match_char) +{ + cregex re = cregex_from("äsdf", 0); + EXPECT_EQ(re.error, 0); + + csview match; + EXPECT_TRUE(cregex_find("äsdf", &re, &match, 0)); + EXPECT_EQ(M_START(re, match), 0); + EXPECT_EQ(M_END(re, match), 5); // ä is two bytes wide + + EXPECT_EQ(cregex_find("zäsdf", &re, &match, 0), 1); + EXPECT_EQ(M_START(re, match), 1); + EXPECT_EQ(M_END(re, match), 6); + + cregex_drop(&re); +} +END_TEST + +START_TEST(compile_match_anchors) +{ + cregex re = cregex_from("^äs.f$", 0); + EXPECT_EQ(re.error, 0); + + csview match; + EXPECT_TRUE(cregex_find("äsdf", &re, &match, 0)); + EXPECT_EQ(M_START(re, match), 0); + EXPECT_EQ(M_END(re, match), 5); + + EXPECT_TRUE(cregex_is_match("äs♥f", &re)); + EXPECT_TRUE(cregex_is_match("äsöf", &re)); + + cregex_drop(re); +} +END_TEST + +START_TEST(compile_match_quantifiers) +{ + c_auto (cregex, re) { + re = cregex_from("ä+", 0); + EXPECT_EQ(re.error, 0); + + csview match; + EXPECT_EQ(cregex_find("ääb", &re, &match, 0), 1); + EXPECT_EQ(M_START(re, match), 0); + EXPECT_EQ(M_END(re, match), 4); + + EXPECT_EQ(cregex_find("bäbb", &re, &match, 0), 1); + EXPECT_EQ(M_START(re, match), 1); + EXPECT_EQ(M_END(re, match), 3); + + EXPECT_FALSE(cregex_find("bbb", &re, &match, 0)); + } + c_auto (cregex, re) { + re = cregex_from("bä*", 0); + EXPECT_EQ(re.error, 0); + + csview match; + EXPECT_EQ(cregex_find("bääb", &re, &match, 0), 1); + EXPECT_EQ(M_START(re, match), 0); + EXPECT_EQ(M_END(re, match), 5); + + EXPECT_EQ(cregex_find("bäbb", &re, &match, 0), 1); + EXPECT_EQ(M_START(re, match), 0); + EXPECT_EQ(M_END(re, match), 3); + + EXPECT_EQ(cregex_find("bbb", &re, &match, 0), 1); + EXPECT_EQ(M_START(re, match), 0); + EXPECT_EQ(M_END(re, match), 1); + } +} +END_TEST + +START_TEST(compile_match_escaped_chars) +{ + cregex re = cregex_from("\\n\\r\\t\\{", 0); + EXPECT_EQ(re.error, 0); + + csview match; + EXPECT_EQ(cregex_find("\n\r\t{", &re, &match, 0), 1); + EXPECT_EQ(cregex_find("\n\r\t", &re, &match, 0), 0); + + cregex_drop(&re); +} +END_TEST + +START_TEST(compile_match_class_simple) +{ + c_auto (cregex, re1, re2, re3) + { + re1 = cregex_from("\\s", 0); + EXPECT_EQ(re1.error, 0); + re2 = cregex_from("\\w", 0); + EXPECT_EQ(re2.error, 0); + re3 = cregex_from("\\D", 0); + EXPECT_EQ(re3.error, 0); + + csview match; + EXPECT_EQ(cregex_find(" ", &re1, &match, 0), 1); + EXPECT_EQ(cregex_find("\r", &re1, &match, 0), 1); + EXPECT_EQ(cregex_find("\n", &re1, &match, 0), 1); + + EXPECT_EQ(cregex_find("a", &re2, &match, 0), 1); + EXPECT_EQ(cregex_find("0", &re2, &match, 0), 1); + EXPECT_EQ(cregex_find("_", &re2, &match, 0), 1); + + EXPECT_EQ( cregex_find("k", &re3, &match, 0), 1); + EXPECT_EQ(cregex_find("0", &re3, &match, 0), 0); + } +} +END_TEST + +START_TEST(compile_match_or) +{ + c_auto (cregex, re, re2) + { + re = cregex_from("as|df", 0); + EXPECT_EQ(re.error, 0); + + csview match; + EXPECT_EQ(cregex_find("as", &re, &match, 0), 1); + EXPECT_EQ(cregex_find("df", &re, &match, 0), 1); + + re2 = cregex_from("(as|df)", 0); + EXPECT_EQ(re2.error, 0); + + EXPECT_EQ(cregex_find(&re2, "as", &re, &match, 0), 1); + EXPECT_EQ(cregex_find(&re2, "df", &re, &match, 0), 1); + } +} +END_TEST + +START_TEST(compile_match_class_complex_0) +{ + cregex re = cregex_from("[asdf]", 0); + EXPECT_EQ(re.error, 0); + + csview match; + EXPECT_EQ(cregex_find("a", &re, &match, 0), 1); + EXPECT_EQ(cregex_find("s", &re, &match, 0), 1); + EXPECT_EQ(cregex_find("d", &re, &match, 0), 1); + EXPECT_EQ(cregex_find("f", &re, &match, 0), 1); + + cregex_drop(&re); +} +END_TEST + +START_TEST(compile_match_class_complex_1) +{ + cregex re = cregex_from("[a-zä0-9öA-Z]", 0); + EXPECT_EQ(re.error, 0); + + csview match; + EXPECT_EQ(cregex_find("a", &re, &match, 0), 1); + EXPECT_EQ(cregex_find("5", &re, &match, 0), 1); + EXPECT_EQ(cregex_find("A", &re, &match, 0), 1); + EXPECT_EQ(cregex_find("ä", &re, &match, 0), 1); + EXPECT_EQ(cregex_find("ö", &re, &match, 0), 1); + + cregex_drop(&re); +} +END_TEST + +START_TEST(compile_match_cap) +{ + cregex re = cregex_from("(abc)d", 0); + EXPECT_EQ(re.error, 0); + + csview match; + EXPECT_EQ(cregex_find("abcd", &re, &match, 0)), 1; + EXPECT_EQ(cregex_find("llljabcdkk", &re, &match, 0), 1); + EXPECT_EQ(cregex_find("abc", &re, &match, 0), 0); + + cregex_drop(&re); +} +END_TEST + +START_TEST(search_all) +{ + c_auto (cregex, re) + { + re = cregex_from("ab", 0); + csview m = {0}; + int res; + + res = cregex_find("ab,ab,ab", &m, CREG_M_NEXT); + EXPECT_TRUE(res==1 && M_START(re, m) == 0); + res = cregex_find("ab,ab,ab", &m, CREG_M_NEXT); + EXPECT_TRUE(res==1 && M_START(re, m) == 3); + res = cregex_find("ab,ab,ab", &m, CREG_M_NEXT); + EXPECT_TRUE(res==1 && M_START(re, m) == 6); + res = cregex_find("ab,ab,ab", &m, CREG_M_NEXT); + EXPECT_NE(res, 1); + } +} +END_TEST + +START_TEST(captures_len) +{ + c_auto (cregex, re) { + cregex re = cregex_from("(ab(cd))(ef)", 0); + EXPECT_EQ(cregex_captures(&re), 3); + } +} +END_TEST + +START_TEST(captures_cap) +{ + c_auto (cregex, re) { + re = cregex_from("(ab)((cd)+)", 0); + EXPECT_EQ(cregex_captures(&re), 3); + + csview cap[5]; + EXPECT_EQ(cregex_find("xxabcdcde", &re, cap, 0), 1); + EXPECT_TRUE(csview_equals(cap.match[0], c_SV("abcdcd"))); + /* + EXPECT_EQ(cap0.end, 8); + EXPECT_EQ(cap1.start, 2); + EXPECT_EQ(cap1.end, 4); + EXPECT_EQ(cap2.start, 4); + EXPECT_EQ(cap2.end, 8); + */ + EXPECT_FALSE(cregex_is_match("abcdcde", &re)); + EXPECT_TRUE(cregex_is_match("abcdcdcd", &re)); + } +} +END_TEST + + +int main() +{ + RUN_TEST(compile_match_char); + RUN_TEST(compile_match_anchors); + RUN_TEST(compile_match_quantifiers); + RUN_TEST(compile_match_escaped_chars); + RUN_TEST(compile_match_class_simple); + RUN_TEST(compile_match_class_complex_0); + RUN_TEST(compile_match_class_complex_1); + RUN_TEST(compile_match_cap); + RUN_TEST(search_all); + RUN_TEST(captures_len); + RUN_TEST(captures_cap); + RUN_TEST(compile_match_or); + return REPORT_TESTS(); +} +#endif + |
