├── .github └── workflows │ └── ci.yaml ├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── README.md ├── algorithm ├── CMakeLists.txt ├── include │ └── cpp-utilities │ │ └── algorithm.h └── test │ ├── CMakeLists.txt │ └── algorithm.cpp ├── arena ├── CMakeLists.txt ├── include │ └── cpp-utilities │ │ └── arena.h └── test │ ├── CMakeLists.txt │ └── arena.cpp ├── bitset ├── CMakeLists.txt └── include │ └── cpp-utilities │ └── bitset.h ├── bitwise ├── CMakeLists.txt └── include │ └── cpp-utilities │ └── bitwise.h ├── container ├── CMakeLists.txt ├── include │ └── cpp-utilities │ │ ├── flat_map.h │ │ ├── flat_set.h │ │ └── lru_cache.h └── test │ ├── CMakeLists.txt │ ├── flat_map │ ├── CMakeLists.txt │ └── flat_map.cpp │ ├── flat_set │ ├── CMakeLists.txt │ └── flat_set.cpp │ └── lru_cache │ ├── CMakeLists.txt │ └── lru_cache.cpp ├── defaults └── CMakeLists.txt ├── fixed ├── CMakeLists.txt ├── include │ └── cpp-utilities │ │ └── fixed.h └── test │ ├── CMakeLists.txt │ └── fixed.cpp ├── hash ├── CMakeLists.txt ├── include │ └── cpp-utilities │ │ ├── crc32.h │ │ ├── md5.h │ │ └── sha1.h └── test │ ├── CMakeLists.txt │ └── test.cpp ├── logger ├── CMakeLists.txt ├── include │ └── cpp-utilities │ │ └── logger.h └── test │ ├── CMakeLists.txt │ └── test.cpp ├── performance ├── CMakeLists.txt ├── include │ └── cpp-utilities │ │ └── time_code.h └── test │ ├── CMakeLists.txt │ └── test.cpp ├── pprint ├── CMakeLists.txt └── include │ └── cpp-utilities │ └── pprint.h ├── programs ├── CMakeLists.txt └── byte_writer.cpp ├── range ├── CMakeLists.txt └── include │ └── cpp-utilities │ └── range.h ├── string ├── CMakeLists.txt ├── include │ └── cpp-utilities │ │ └── string.h └── test │ ├── CMakeLists.txt │ └── test.cpp ├── thread_pool ├── CMakeLists.txt ├── include │ └── cpp-utilities │ │ └── thread_pool.h └── test │ ├── CMakeLists.txt │ └── thread_pool.cpp ├── uint128 ├── CMakeLists.txt ├── include │ └── cpp-utilities │ │ └── uint128.h └── test │ ├── CMakeLists.txt │ └── uint128.cpp └── uuid ├── CMakeLists.txt ├── include └── cpp-utilities │ └── uuid.h └── test ├── CMakeLists.txt └── uuid.cpp /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | ci: 9 | runs-on: ubuntu-22.04 10 | strategy: 11 | matrix: 12 | # @see https://stackoverflow.com/a/68940067 13 | compiler: [{cc: clang-15, cxx: clang++-15}, {cc: gcc, cxx: g++}] 14 | std: [14, 17] 15 | steps: 16 | - uses: actions/checkout@v3 17 | 18 | - name: Install dependencies 19 | run: | 20 | sudo apt-get -y update && sudo apt-get -y install \ 21 | clang-15 \ 22 | libboost-all-dev 23 | 24 | - name: Configure 25 | run: | 26 | CC=${{ matrix.compiler.cc }} CXX=${{ matrix.compiler.cxx }} cmake -DCMAKE_CXX_STANDARD=${{ matrix.std }} . 27 | 28 | - name: Build 29 | run: | 30 | make 31 | 32 | - name: Test 33 | run: | 34 | make test 35 | 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | .vscode/ 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: focal 2 | 3 | addons: 4 | apt: 5 | packages: 6 | - libboost-dev 7 | - clang-12 8 | 9 | env: 10 | - CC=clang CXX=clang++-12 STD=14 11 | - CC=gcc CXX=g++ STD=14 12 | - CC=clang CXX=clang++-12 STD=17 13 | - CC=gcc CXX=g++ STD=17 14 | 15 | script: 16 | - cmake -DCMAKE_CXX_STANDARD=$STD . 17 | - make 18 | - make test 19 | 20 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # https://github.com/eteran/cpp-utilities 2 | cmake_minimum_required(VERSION 3.5) 3 | project(cpp-utilities LANGUAGES CXX) 4 | 5 | enable_testing() 6 | 7 | add_subdirectory(algorithm) 8 | add_subdirectory(arena) 9 | add_subdirectory(bitset) 10 | add_subdirectory(bitwise) 11 | add_subdirectory(container) 12 | add_subdirectory(defaults) 13 | add_subdirectory(fixed) 14 | add_subdirectory(hash) 15 | add_subdirectory(logger) 16 | add_subdirectory(performance) 17 | add_subdirectory(pprint) 18 | add_subdirectory(programs) 19 | add_subdirectory(range) 20 | add_subdirectory(string) 21 | add_subdirectory(thread_pool) 22 | add_subdirectory(uint128) 23 | add_subdirectory(uuid) 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Evan Teran 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cpp-utilities 2 | Miscellaneous C++11/C++14/C++17 utility classes and functions 3 | 4 | ### Hash Library 5 | Found in [md5.h](hash/include/cpp-utilities/md5.h) and [sha1.h](hash/include/cpp-utilities/sha1.h) 6 | 7 | As you might expect, this is an implementation of the MD5 and SHA1 hashing algorithms. The usage of both are identical and designed for ease of use. 8 | 9 | // create an MD5 object and give it some data 10 | hash::md5 hasher("Hello World"); 11 | hasher.update('!'); // you can stream new values in as you please 12 | auto digest = hasher.finalize(); // create a digest for "Hello World!" 13 | std::cout << digest.to_string() << std::endl; 14 | 15 | **NOTE:** One thing to note that I feel is a nice design feature. `finalize()` does not modify the internal state of the hasher, it returns a copy of the digest in finalized form. This means that you can call `finalize()`, then continue to append new data into the hasher, call `finalize()` again, and get correct hashes. 16 | 17 | ### Arena Allocator 18 | 19 | Found in [arena.h](arena/include/cpp-utilities/arena.h). This is an implementation of a very efficient fixed block size arena allocator. It allows allocating and freeing back to the arena (if you want to, it isn't necessary), and will use one of two strategies depending on the size of blocks you need. If the blocks are smaller than the size of a pointer, and the arena is relatively small, then it will use a bitmap along with compiler intrinsics to find free blocks. If the the blocks are at least as large as a pointer, it will use a freelist implementation. Template deduction will choose the best backend for you. 20 | 21 | Usage is **very** simple: 22 | 23 | // create an arena of 1024 64-bit blocks 24 | auto arena = memory::make_arena(); 25 | auto p1 = arena.allocate(); // get a single uint64_t sized block 26 | arena.release(p1); // free that block back into the system 27 | // (not necessary, the arena will clean itself up) 28 | 29 | On my system , the allocate function when using the freelist strategy, allocate was **as few a 5 instructions**. Some of which were simple `nullptr` checks. Since this is a low level allocator, constructors are not called, you get a block of memory suitably aligned and sized for the type specified. 30 | 31 | ### Bitset Utility Functions 32 | 33 | Found in [bitset.h](bitset/include/cpp-utilities/bitset.h). This header provides a nice utility function to find the first set bit in a bitset. When possible using GCC intrinsics to do it in O(1) time, but falling back on an iterative implementation when this is not possible. 34 | 35 | std::bitset<32> bs; 36 | bs[4] = true; 37 | bs[10] = true; 38 | int bit_l = bitset::find_first(bs); // returns 4 39 | int bit_h = bitset::find_last(bs); // returns 10 40 | 41 | The function is defined to return `bitset.size()` when no bits are set, this is similar to containers returning `.end()` for find operations. 42 | 43 | ### Bitwise Operations 44 | 45 | [bitwise.h](bitwise/include/cpp-utilities/bitwise.h) provides efficient and type safe rotation operations that will work with any integral type. A future version may be implemented using intrinsics, but for now it's a fairly straight forward shift and mask solution. Impressively, gcc will often reduce this to a single `rol` instruction when optimizations are enabled! 46 | 47 | int x = 5; 48 | int y = bitwise::rotate_right(x, 15); 49 | int x = bitwise::rotate_left(x, 20); 50 | 51 | ### String Utility Functions 52 | 53 | [string.h](string/include/cpp-utilities/string.h) provides several common string functions such as trimming, upper/lower casing, testing what it starts and ends with, etc. 54 | 55 | ### Algorithms 56 | 57 | [algorithm.h](algorithm/include/cpp-utilities/algorithm.h) is a set of algorithms for general purpose use. Currently there are implementations of variadic min and max functions which are compile time. For example: 58 | 59 | int n = algorithm::static_max(1, 2, 3, 10, 5, 6); 60 | printf("%d\n", n); // prints 10 61 | 62 | Because it is a compile time constant, you can also safely use it in templates as well. For example: 63 | 64 | template 65 | struct Foo { 66 | static const int value = N * 2; 67 | }; 68 | 69 | int main() { 70 | int n = Foo::value; 71 | printf("%d\n", n); // prints 20 72 | } 73 | 74 | Of course your compiler will have to have good support for `constexpr` :-). 75 | 76 | ### Pretty Printers 77 | 78 | [pprint.h](pprint/include/cpp-utilities/pprint.h) is a set of utility functions to print common data structures in a "pretty" way. Similar to PHP's `print_r()`. Usage looks like this: 79 | 80 | std::vector v = { 1, 2, 3, 4, 5, 6, 7 }; 81 | std::cout << pprint::to_string(v) << std::endl; 82 | 83 | Which will print: 84 | 85 | std::vector<> 86 | ( 87 | [0] => 1 88 | [1] => 2 89 | [2] => 3 90 | [3] => 4 91 | [4] => 5 92 | [5] => 6 93 | [6] => 7 94 | ) 95 | 96 | `std::vector`, `std::list`, `std::deque`, `std::set`, `std::map` are all supported. Additionally, complex nesting of containers should work just fine. For example, a list of vectors: 97 | 98 | std::list> v = { {1, 2, 3}, {4, 5, 6, 7} }; 99 | std::cout << pprint::to_string(v) << std::endl; 100 | 101 | Will print: 102 | 103 | std::list<> 104 | ( 105 | [0] => std::vector<> 106 | ( 107 | [0] => 1 108 | [1] => 2 109 | [2] => 3 110 | ) 111 | [1] => std::vector<> 112 | ( 113 | [0] => 4 114 | [1] => 5 115 | [2] => 6 116 | [3] => 7 117 | ) 118 | ) 119 | 120 | 121 | ### Fixed Point Math 122 | [fixed.h](fixed/include/cpp-utilities/fixed.h) 123 | 124 | This is a Fixed Point math class. It supports all combinations which add up to a native data types (8.8/16.16/24.8/etc). The template parameters are the number of bits to use as the base type for both the integer and fractional portions, invalid combinations will yield a compiler error; the current implementation makes use of `static assert` to make this more readable. It should be a nice drop in replacement for native `float` types. Here's an example usage: 125 | 126 | typedef numeric::fixed<16, 16> fixed; 127 | fixed f; 128 | 129 | This will declare a 16.16 fixed point number. Operators are provided though the use of boost::operators. multiplication and division are implemented in free functions named `numeric::multiply` and `numeric::divide` which use `std::enable_if` to choose the best option. If a larger type is available, it will use the accurate and fast scaled math version. If there is not a larger type available, then it will fall back on the slower multiply and emulated divide (which unfortunately has less precision). This system allows the user to specialize the multiplication and division as needed. 130 | 131 | 132 | ### Flat associative containers 133 | [flat_map.h](container/include/cpp-utilities/flat_map.h) 134 | 135 | This is an implementation of a `std::map` but using a contiguous data structure (`std::vector`) as the underlying storage. The elements are stored sorted by key, so lookup should be as efficient as a `binary_search`, and iteration is as efficient as accessing a `std::vector`. 136 | 137 | [flat_set.h](container/include/cpp-utilities/flat_set.h) 138 | 139 | This is an implementation of a `std::set` but using a contiguous data structure (`std::vector`) as the underlying storage. The elements are stored sorted by key, so lookup should be as efficient as a `binary_search`, and iteration is as efficient as accessing a `std::vector`. 140 | -------------------------------------------------------------------------------- /algorithm/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | add_library(cpp-utilities-algorithm INTERFACE) 4 | add_library(cpp-utilities::algorithm ALIAS cpp-utilities-algorithm) 5 | 6 | target_include_directories(cpp-utilities-algorithm 7 | INTERFACE 8 | ${CMAKE_CURRENT_LIST_DIR}/include 9 | ) 10 | 11 | add_subdirectory(test) 12 | 13 | -------------------------------------------------------------------------------- /algorithm/include/cpp-utilities/algorithm.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Evan Teran 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef UTILITIES_ALGORITHM_HPP 26 | #define UTILITIES_ALGORITHM_HPP 27 | 28 | #include 29 | 30 | namespace algorithm { 31 | 32 | template 33 | constexpr T static_max(T n) noexcept { 34 | return n; 35 | } 36 | 37 | template 38 | constexpr T static_max(T n, Args... args) noexcept { 39 | return n > static_max(args...) ? n : static_max(args...); 40 | } 41 | 42 | template 43 | constexpr T static_min(T n) noexcept { 44 | return n; 45 | } 46 | 47 | template 48 | constexpr T static_min(T n, Args... args) noexcept { 49 | return n < static_min(args...) ? n : static_min(args...); 50 | } 51 | 52 | } 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /algorithm/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | add_executable(cpp-utilities-algorithm-test 4 | algorithm.cpp 5 | ) 6 | 7 | target_link_libraries(cpp-utilities-algorithm-test 8 | PRIVATE 9 | cpp-utilities::defaults 10 | cpp-utilities::algorithm 11 | ) 12 | 13 | set_property(TARGET cpp-utilities-algorithm-test PROPERTY CXX_STANDARD 17) 14 | 15 | add_test( 16 | NAME cpp-utilities-algorithm-test 17 | COMMAND $ 18 | ) 19 | 20 | -------------------------------------------------------------------------------- /algorithm/test/algorithm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | static_assert(algorithm::static_max(1, 3, 5, 8, 2, 5, 9) == 9); 5 | static_assert(algorithm::static_min(1, 3, 5, 8, 2, 5, 9) == 1); 6 | } 7 | -------------------------------------------------------------------------------- /arena/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | cmake_minimum_required(VERSION 3.5) 3 | 4 | add_library(cpp-utilities-arena INTERFACE) 5 | add_library(cpp-utilities::arena ALIAS cpp-utilities-arena) 6 | 7 | target_include_directories(cpp-utilities-arena 8 | INTERFACE 9 | ${CMAKE_CURRENT_LIST_DIR}/include 10 | ) 11 | 12 | 13 | target_link_libraries(cpp-utilities-arena 14 | INTERFACE 15 | cpp-utilities::bitset 16 | ) 17 | 18 | 19 | add_subdirectory(test) 20 | 21 | -------------------------------------------------------------------------------- /arena/include/cpp-utilities/arena.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Evan Teran 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef UTILITY_ARENA_HPP_ 26 | #define UTILITY_ARENA_HPP_ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #define ARENA_ALLOCATOR_PURIFY 38 | 39 | namespace memory { 40 | namespace detail { 41 | 42 | struct bitset_strategy_tag {}; 43 | struct linked_strategy_tag {}; 44 | 45 | // simplify storage management 46 | template 47 | class malloc_storage { 48 | public: 49 | malloc_storage() noexcept 50 | : p_(reinterpret_cast(malloc(sizeof(T) * Count))) { 51 | } 52 | 53 | ~malloc_storage() noexcept { 54 | free(p_); 55 | } 56 | 57 | malloc_storage(const malloc_storage &) = delete; 58 | malloc_storage &operator=(const malloc_storage &) = delete; 59 | 60 | malloc_storage(malloc_storage &&other) noexcept 61 | : p_(std::exchange(other.p_, nullptr)) { 62 | } 63 | 64 | malloc_storage &operator=(malloc_storage &&rhs) noexcept { 65 | if (this != &rhs) { 66 | p_ = std::exchange(rhs.p_, nullptr); 67 | } 68 | return *this; 69 | } 70 | 71 | T &operator[](size_t index) noexcept { 72 | return p_[index]; 73 | } 74 | 75 | private: 76 | T *p_ = nullptr; 77 | }; 78 | 79 | template 80 | class arena_allocator; 81 | 82 | template 83 | class arena_allocator { 84 | public: 85 | arena_allocator() noexcept { 86 | freelist_.set(); 87 | } 88 | 89 | arena_allocator(arena_allocator &&) noexcept = default; 90 | arena_allocator &operator=(arena_allocator &&) noexcept = default; 91 | arena_allocator(const arena_allocator &) = delete; 92 | arena_allocator &operator=(const arena_allocator &) = delete; 93 | ~arena_allocator() = default; 94 | 95 | public: 96 | void release(void *ptr) noexcept { 97 | if (ptr) { 98 | assert(ptr >= &storage_[0] && "Attempting to release invalid pointer"); 99 | assert(ptr < &storage_[Count] && "Attempting to release invalid pointer"); 100 | 101 | const int index = (reinterpret_cast(ptr) - &storage_[0]); 102 | 103 | assert(!freelist_[index] && "Double free detected"); 104 | freelist_.flip(index); 105 | } 106 | } 107 | 108 | void *allocate() noexcept { 109 | const int index = bitset::find_first(freelist_); 110 | if (index == Count) { 111 | return nullptr; 112 | } 113 | 114 | freelist_[index].flip(); 115 | 116 | T *const p = &storage_[index]; 117 | 118 | #ifdef ARENA_ALLOCATOR_PURIFY 119 | // this is the small object allocator, so it's pretty efficient to 120 | // always clear out the storage :-) 121 | memset(p, 0, sizeof(T)); 122 | #endif 123 | return p; 124 | } 125 | 126 | private: 127 | malloc_storage storage_; 128 | std::bitset freelist_; 129 | }; 130 | 131 | template 132 | class arena_allocator { 133 | static_assert(sizeof(T) >= sizeof(void *), "Linked strategy can only be used for objects larger than or equal to the size of a pointer"); 134 | 135 | private: 136 | struct node { 137 | node *next; 138 | }; 139 | 140 | public: 141 | arena_allocator() noexcept 142 | : freelist_(nullptr) { 143 | for (size_t i = 0; i < Count; ++i) { 144 | release(&storage_[i]); 145 | } 146 | } 147 | 148 | ~arena_allocator() = default; 149 | 150 | public: 151 | arena_allocator(arena_allocator &&other) 152 | : storage_(std::move(other.storage_)), freelist_(std::exchange(other.freelist_, nullptr)) { 153 | } 154 | 155 | arena_allocator &operator=(arena_allocator &&rhs) noexcept { 156 | if (this != &rhs) { 157 | storage_ = std::move(rhs.storage_); 158 | freelist_ = std::exchange(rhs.freelist_, nullptr); 159 | } 160 | return *this; 161 | } 162 | 163 | private: 164 | arena_allocator(const arena_allocator &) = delete; 165 | arena_allocator &operator=(const arena_allocator &) = delete; 166 | 167 | public: 168 | void release(void *ptr) noexcept { 169 | if (ptr) { 170 | assert(ptr >= &storage_[0] && "Attempting to release invalid pointer"); 171 | assert(ptr < &storage_[Count] && "Attempting to release invalid pointer"); 172 | assert((reinterpret_cast(ptr) & (sizeof(void *) - 1)) == 0 && "Attempting to release misaligned pointer"); 173 | 174 | node *const p = reinterpret_cast(ptr); 175 | 176 | // TODO: in debug mode, detect a double free... not sure how this can be 177 | // done efficiently with a linked list. 178 | 179 | p->next = std::exchange(freelist_, p); 180 | } 181 | } 182 | 183 | void *allocate() noexcept { 184 | if (!freelist_) { 185 | return nullptr; 186 | } 187 | 188 | node *const p = std::exchange(freelist_, freelist_->next); 189 | #ifdef ARENA_ALLOCATOR_PURIFY 190 | // avoid information disclosure bug 191 | p->next = nullptr; 192 | #endif 193 | return reinterpret_cast(p); 194 | } 195 | 196 | private: 197 | malloc_storage storage_; 198 | node *freelist_; 199 | }; 200 | } 201 | 202 | template 203 | detail::arena_allocator make_arena(typename std::enable_if<(sizeof(T) >= sizeof(void *)) && (Count > sizeof(size_t) * CHAR_BIT)>::type * = nullptr) { 204 | return detail::arena_allocator(); 205 | } 206 | 207 | template 208 | detail::arena_allocator make_arena(typename std::enable_if<(sizeof(T) < sizeof(void *)) || (Count <= sizeof(size_t) * CHAR_BIT)>::type * = nullptr) { 209 | return detail::arena_allocator(); 210 | } 211 | 212 | } 213 | 214 | #endif 215 | -------------------------------------------------------------------------------- /arena/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | cmake_minimum_required(VERSION 3.5) 3 | 4 | add_executable(cpp-utilities-arena-test 5 | arena.cpp 6 | ) 7 | 8 | target_link_libraries(cpp-utilities-arena-test 9 | PRIVATE 10 | cpp-utilities::arena 11 | cpp-utilities::defaults 12 | ) 13 | 14 | 15 | add_test( 16 | NAME cpp-utilities-arena-test 17 | COMMAND $ 18 | ) 19 | 20 | -------------------------------------------------------------------------------- /arena/test/arena.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() { 5 | 6 | using T = uint64_t; 7 | 8 | auto arena = memory::make_arena(); 9 | 10 | auto x1 = static_cast(arena.allocate()); 11 | printf("Allocated Address = %p\n", x1); 12 | 13 | auto x2 = static_cast(arena.allocate()); 14 | printf("Allocated Address = %p\n", x2); 15 | 16 | arena.release(x1); 17 | arena.release(x2); 18 | } 19 | -------------------------------------------------------------------------------- /bitset/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | add_library(cpp-utilities-bitset INTERFACE) 4 | add_library(cpp-utilities::bitset ALIAS cpp-utilities-bitset) 5 | 6 | 7 | target_include_directories(cpp-utilities-bitset 8 | INTERFACE 9 | ${CMAKE_CURRENT_LIST_DIR}/include 10 | ) 11 | 12 | -------------------------------------------------------------------------------- /bitset/include/cpp-utilities/bitset.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Evan Teran 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef UTILITY_BITSET_HPP_ 26 | #define UTILITY_BITSET_HPP_ 27 | 28 | #include 29 | #include 30 | 31 | namespace bitset { 32 | 33 | template 34 | int find_first(const std::bitset &bs) noexcept { 35 | if (bs.none()) { 36 | return N; 37 | } 38 | 39 | std::size_t i; 40 | for (i = 0; i < N; ++i) { 41 | if (bs[i]) { 42 | break; 43 | } 44 | } 45 | 46 | return i; 47 | } 48 | 49 | template 50 | int find_last(const std::bitset &bs) noexcept { 51 | if (bs.none()) { 52 | return N; 53 | } 54 | 55 | size_t i = N; 56 | while (--i) { 57 | if (bs[i]) { 58 | break; 59 | } 60 | } 61 | 62 | return i; 63 | } 64 | 65 | #if defined(__GNUC__) 66 | template <> 67 | inline int find_first(const std::bitset<32> &bs) noexcept { 68 | if (bs.none()) { 69 | return bs.size(); 70 | } 71 | return __builtin_ctz(static_cast(bs.to_ulong())); 72 | } 73 | 74 | template <> 75 | inline int find_last(const std::bitset<32> &bs) noexcept { 76 | if (bs.none()) { 77 | return bs.size(); 78 | } 79 | return bs.size() - __builtin_clz(static_cast(bs.to_ulong())) - 1; 80 | } 81 | 82 | #if __cplusplus >= 201103L 83 | template <> 84 | inline int find_first(const std::bitset<64> &bs) noexcept { 85 | if (bs.none()) { 86 | return bs.size(); 87 | } 88 | return __builtin_ctzll(static_cast(bs.to_ullong())); 89 | } 90 | 91 | template <> 92 | inline int find_last(const std::bitset<64> &bs) noexcept { 93 | if (bs.none()) { 94 | return bs.size(); 95 | } 96 | return bs.size() - __builtin_clzll(static_cast(bs.to_ullong())) - 1; 97 | } 98 | #endif 99 | #endif 100 | 101 | } 102 | 103 | #endif 104 | -------------------------------------------------------------------------------- /bitwise/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | add_library(cpp-utilities-bitwise INTERFACE) 4 | add_library(cpp-utilities::bitwise ALIAS cpp-utilities-bitwise) 5 | 6 | target_include_directories(cpp-utilities-bitwise 7 | INTERFACE 8 | ${CMAKE_CURRENT_LIST_DIR}/include 9 | ) 10 | 11 | -------------------------------------------------------------------------------- /bitwise/include/cpp-utilities/bitwise.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Evan Teran 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef UTILITIES_BITWISE_HPP_ 26 | #define UTILITIES_BITWISE_HPP_ 27 | 28 | #include // for CHAR_BIT 29 | 30 | namespace bitwise { 31 | 32 | /* 33 | * the reason why we need the "mask" constant is 34 | * because some platforms make >> on negative values 35 | * arithmetic (which shifts in the sign bit, not 0) 36 | * using the mask allows this to work with both signed 37 | * and unsigned types 38 | */ 39 | template 40 | T rotate_left(T v, int n) noexcept { 41 | constexpr unsigned int bits = CHAR_BIT * sizeof(T); 42 | const T mask = ~(T(-1) << n); 43 | 44 | return (v << n) | ((v >> (bits - n)) & mask); 45 | } 46 | 47 | template 48 | T rotate_right(T v, int n) noexcept { 49 | return rotate_left(v, -n); 50 | } 51 | 52 | } 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /container/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | add_library(cpp-utilities-container INTERFACE) 4 | add_library(cpp-utilities::container ALIAS cpp-utilities-container) 5 | 6 | target_include_directories(cpp-utilities-container 7 | INTERFACE 8 | ${CMAKE_CURRENT_LIST_DIR}/include 9 | ) 10 | 11 | add_subdirectory(test) 12 | 13 | 14 | -------------------------------------------------------------------------------- /container/include/cpp-utilities/flat_map.h: -------------------------------------------------------------------------------- 1 | #ifndef FLAT_MAP_H_ 2 | #define FLAT_MAP_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | template , class Allocator = std::allocator>> 13 | class flat_map { 14 | private: 15 | using mutable_value_type = std::pair; 16 | 17 | public: 18 | using key_type = Key; 19 | using mapped_type = T; 20 | using value_type = std::pair; 21 | using storage_type = std::vector; 22 | using size_type = typename storage_type::size_type; 23 | using difference_type = typename storage_type::difference_type; 24 | using key_compare = Compare; 25 | using allocator_type = Allocator; 26 | using reference = typename storage_type::reference; 27 | using const_reference = typename storage_type::const_reference; 28 | using pointer = typename storage_type::pointer; 29 | using const_pointer = typename storage_type::const_pointer; 30 | using iterator = typename storage_type::iterator; 31 | using const_iterator = typename storage_type::const_iterator; 32 | using reverse_iterator = typename storage_type::reverse_iterator; 33 | using const_reverse_iterator = typename storage_type::const_reverse_iterator; 34 | 35 | public: 36 | class value_compare { 37 | friend class flat_map; 38 | 39 | protected: 40 | value_compare(Compare c) 41 | : comp(c) { 42 | } 43 | 44 | public: 45 | bool operator()(const value_type &lhs, const value_type &rhs) const { 46 | return comp(lhs.first, rhs.first); 47 | } 48 | 49 | public: 50 | // for convenience 51 | bool operator()(const key_type &lhs, const value_type &rhs) const { 52 | return comp(lhs, rhs.first); 53 | } 54 | 55 | bool operator()(const value_type &lhs, const key_type &rhs) const { 56 | return comp(lhs.first, rhs); 57 | } 58 | 59 | bool operator()(const key_type &lhs, const key_type &rhs) const { 60 | return comp(lhs, rhs); 61 | } 62 | 63 | protected: 64 | Compare comp; 65 | }; 66 | 67 | public: 68 | flat_map() 69 | : flat_map(Compare()) { 70 | } 71 | 72 | explicit flat_map(const Compare &comp, const Allocator &alloc = Allocator()) 73 | : comp_(comp), storage_(alloc) { 74 | } 75 | 76 | template 77 | flat_map(In first, In last, const Compare &comp = Compare(), const Allocator &alloc = Allocator()) 78 | : comp_(comp), storage_(alloc) { 79 | insert(first, last); 80 | } 81 | 82 | template 83 | flat_map(In first, In last, const Allocator &alloc) 84 | : storage_(alloc) { 85 | insert(first, last); 86 | } 87 | 88 | flat_map(const flat_map &other) 89 | : comp_(other.comp_), storage_(other.storage_) { 90 | } 91 | 92 | flat_map(const flat_map &other, const Allocator &alloc) 93 | : comp_(other.comp_), storage_(other.storage_, alloc) { 94 | } 95 | 96 | flat_map(const flat_map &&other) 97 | : comp_(std::move(other.comp_)), storage_(std::move(other.storage_)) { 98 | } 99 | 100 | flat_map(const flat_map &&other, const Allocator &alloc) 101 | : comp_(std::move(other.comp_)), storage_(std::move(other.storage_), alloc) { 102 | } 103 | 104 | flat_map(std::initializer_list init, const Compare &comp = Compare(), const Allocator &alloc = Allocator()) 105 | : comp_(comp), storage_(alloc) { 106 | insert(init); 107 | } 108 | 109 | flat_map(std::initializer_list init, const Allocator &alloc) 110 | : comp_(Compare()), storage_(alloc) { 111 | insert(init); 112 | } 113 | 114 | ~flat_map() = default; 115 | 116 | public: 117 | flat_map &operator=(const flat_map &other) { 118 | if (this != &other) { 119 | flat_map(other).swap(*this); 120 | } 121 | return *this; 122 | } 123 | 124 | flat_map &operator=(flat_map &&other) { 125 | if (this != &other) { 126 | flat_map(std::move(other)).swap(*this); 127 | } 128 | return *this; 129 | } 130 | 131 | flat_map &operator=(std::initializer_list ilist) { 132 | flat_map(ilist).swap(*this); 133 | return *this; 134 | } 135 | 136 | public: 137 | allocator_type get_allocator() const { 138 | return storage_.get_allocator(); 139 | } 140 | 141 | public: 142 | T &at(const Key &key) { 143 | using std::begin; 144 | using std::end; 145 | 146 | auto it = std::lower_bound(begin(storage_), end(storage_), key, comp_); 147 | if (it != end(storage_) && !comp_(key, *it)) { 148 | // found! 149 | return it->second; 150 | } else { 151 | throw std::out_of_range("out_of_range"); 152 | } 153 | } 154 | 155 | const T &at(const Key &key) const { 156 | using std::begin; 157 | using std::end; 158 | 159 | auto it = std::lower_bound(begin(storage_), end(storage_), key, comp_); 160 | if (it != end(storage_) && !comp_(key, *it)) { 161 | // found! 162 | return it->second; 163 | } else { 164 | throw std::out_of_range("out_of_range"); 165 | } 166 | } 167 | 168 | public: 169 | T &operator[](const Key &key) { 170 | using std::begin; 171 | using std::end; 172 | 173 | auto it = std::lower_bound(begin(storage_), end(storage_), key, comp_); 174 | if (it != end(storage_) && !comp_(key, *it)) { 175 | // found! 176 | return it->second; 177 | } else { 178 | // create it 179 | auto n = storage_.emplace(it, key, T()); 180 | return n->second; 181 | } 182 | } 183 | 184 | T &operator[](Key &&key) { 185 | using std::begin; 186 | using std::end; 187 | 188 | auto it = std::lower_bound(begin(storage_), end(storage_), key, comp_); 189 | if (it != end(storage_) && !comp_(key, *it)) { 190 | // found! 191 | return it->second; 192 | } else { 193 | // create it 194 | auto n = storage_.emplace(it, std::forward(key), T()); 195 | return n->second; 196 | } 197 | } 198 | 199 | public: 200 | iterator begin() { 201 | using std::begin; 202 | return begin(storage_); 203 | } 204 | 205 | const_iterator begin() const { 206 | using std::begin; 207 | return begin(storage_); 208 | } 209 | 210 | const_iterator cbegin() const { 211 | using std::cbegin; 212 | return cbegin(storage_); 213 | } 214 | 215 | iterator end() { 216 | using std::end; 217 | return end(storage_); 218 | } 219 | 220 | const_iterator end() const { 221 | using std::end; 222 | return end(storage_); 223 | } 224 | 225 | const_iterator cend() const { 226 | using std::cend; 227 | return cend(storage_); 228 | } 229 | 230 | reverse_iterator rbegin() { 231 | using std::rbegin; 232 | return rbegin(storage_); 233 | } 234 | 235 | const_reverse_iterator rbegin() const { 236 | using std::rbegin; 237 | return rbegin(storage_); 238 | } 239 | 240 | const_reverse_iterator crbegin() const { 241 | using std::crbegin; 242 | return crbegin(storage_); 243 | } 244 | 245 | reverse_iterator rend() { 246 | using std::rend; 247 | return rend(storage_); 248 | } 249 | 250 | const_reverse_iterator rend() const { 251 | using std::rend; 252 | return rend(storage_); 253 | } 254 | 255 | const_reverse_iterator crend() const { 256 | using std::crend; 257 | return crend(storage_); 258 | } 259 | 260 | public: 261 | bool empty() const { 262 | return storage_.empty(); 263 | } 264 | 265 | size_type size() const { 266 | return storage_.size(); 267 | } 268 | 269 | size_type max_size() const { 270 | return storage_.max_size(); 271 | } 272 | 273 | public: 274 | void clear() { 275 | storage_.clear(); 276 | } 277 | 278 | public: 279 | std::pair insert(const value_type &value) { 280 | using std::begin; 281 | using std::end; 282 | auto it = std::lower_bound(begin(storage_), end(storage_), value, comp_); 283 | if (it != end(storage_) && !comp_(value, *it)) { 284 | // found! 285 | return std::make_pair(it, false); 286 | } else { 287 | // create it 288 | auto n = storage_.insert(it, value); 289 | return std::make_pair(n, true); 290 | } 291 | } 292 | 293 | template 294 | std::pair insert(P &&value) { 295 | using std::begin; 296 | using std::end; 297 | auto it = std::lower_bound(begin(storage_), end(storage_), value, comp_); 298 | if (it != end(storage_) && !comp_(value, *it)) { 299 | // found! 300 | return std::make_pair(it, false); 301 | } else { 302 | // create it 303 | auto n = storage_.insert(it, std::forward

(value)); 304 | return std::make_pair(n, true); 305 | } 306 | } 307 | 308 | std::pair insert(value_type &&value) { 309 | using std::begin; 310 | using std::end; 311 | auto it = std::lower_bound(begin(storage_), end(storage_), value, comp_); 312 | if (it != end(storage_) && !comp_(value, *it)) { 313 | // found! 314 | return std::make_pair(it, false); 315 | } else { 316 | // create it 317 | auto n = storage_.insert(it, std::move(value)); 318 | return std::make_pair(n, true); 319 | } 320 | } 321 | 322 | std::pair insert(const_iterator hint, const value_type &value) { 323 | (void)hint; 324 | return insert(value); 325 | } 326 | 327 | template 328 | std::pair insert(const_iterator hint, P &&value) { 329 | (void)hint; 330 | return insert(std::forward

(value)); 331 | } 332 | 333 | std::pair insert(const_iterator hint, value_type &&value) { 334 | (void)hint; 335 | return insert(std::move(value)); 336 | } 337 | 338 | template 339 | void insert(In first, In last) { 340 | while (first != last) { 341 | insert(*first++); 342 | } 343 | } 344 | 345 | void insert(std::initializer_list ilist) { 346 | for (auto &&value : ilist) { 347 | insert(std::move(value)); 348 | } 349 | } 350 | 351 | public: 352 | template 353 | std::pair insert_or_assign(const key_type &key, M &&obj) { 354 | auto it = insert(key); 355 | if (it.second) { 356 | it.first->second = std::forward(obj); 357 | } 358 | return it; 359 | } 360 | 361 | template 362 | std::pair insert_or_assign(key_type &&key, M &&obj) { 363 | auto it = insert(std::move(key)); 364 | if (it.second) { 365 | it.first->second = std::forward(obj); 366 | } 367 | return it; 368 | } 369 | 370 | template 371 | std::pair insert_or_assign(const_iterator hint, const key_type &key, M &&obj) { 372 | auto it = insert(hint, key); 373 | if (it.second) { 374 | it.first->second = std::forward(obj); 375 | } 376 | return it; 377 | } 378 | 379 | template 380 | std::pair insert_or_assign(const_iterator hint, key_type &&key, M &&obj) { 381 | auto it = insert(hint, key); 382 | if (it.second) { 383 | it.first->second = std::forward(obj); 384 | } 385 | return it; 386 | } 387 | 388 | public: 389 | // TODO: try_emplace 390 | template 391 | std::pair emplace(Args &&...args) { 392 | using std::begin; 393 | using std::end; 394 | 395 | value_type temp(std::forward(args)...); 396 | 397 | auto it = std::lower_bound(begin(storage_), end(storage_), temp, comp_); 398 | if (it != end(storage_) && !comp_(temp, *it)) { 399 | // found! 400 | return std::make_pair(it, false); 401 | } else { 402 | // create it 403 | auto n = storage_.emplace(it, std::move(temp)); 404 | return std::make_pair(n, true); 405 | } 406 | } 407 | 408 | template 409 | std::pair emplace_hint(const_iterator hint, Args &&...args) { 410 | 411 | (void)hint; 412 | 413 | using std::begin; 414 | using std::end; 415 | 416 | value_type temp(std::forward(args)...); 417 | 418 | auto it = std::lower_bound(begin(storage_), end(storage_), temp, comp_); 419 | if (it != end(storage_) && !comp_(temp, *it)) { 420 | // found! 421 | return std::make_pair(it, false); 422 | } else { 423 | // create it 424 | auto n = storage_.emplace(it, std::move(temp)); 425 | return std::make_pair(n, true); 426 | } 427 | } 428 | 429 | public: 430 | iterator erase(const_iterator pos) { 431 | return storage_.erase(pos); 432 | } 433 | 434 | iterator erase(iterator pos) { 435 | return storage_.erase(pos); 436 | } 437 | 438 | iterator erase(const_iterator first, const_iterator last) { 439 | return storage_.erase(first, last); 440 | } 441 | 442 | size_type erase(const key_type &key) { 443 | size_type c = 0; 444 | auto r = equal_range(key); 445 | auto first = r.first; 446 | auto second = r.second; 447 | while (first != second) { 448 | erase(first++); 449 | ++c; 450 | } 451 | return c; 452 | } 453 | 454 | public: 455 | void swap(flat_map &other) { 456 | using std::swap; 457 | swap(comp_, other.comp_); 458 | swap(storage_, other.storage_); 459 | } 460 | 461 | public: 462 | size_type count(const Key &key) const { 463 | size_type c = 0; 464 | using std::begin; 465 | using std::end; 466 | 467 | auto it = std::lower_bound(begin(storage_), end(storage_), key, comp_); 468 | while (it != end(storage_) && !comp_(key, *it)) { 469 | ++c; 470 | } 471 | 472 | return c; 473 | } 474 | 475 | template 476 | size_type count(const K &key) const { 477 | size_type c = 0; 478 | using std::begin; 479 | using std::end; 480 | auto it = std::lower_bound(begin(storage_), end(storage_), key, comp_); 481 | while (it != end(storage_) && !comp_(key, *it)) { 482 | ++c; 483 | } 484 | 485 | return c; 486 | } 487 | 488 | public: 489 | iterator find(const Key &key) { 490 | using std::begin; 491 | using std::end; 492 | auto it = std::lower_bound(begin(storage_), end(storage_), key, comp_); 493 | if (it != end(storage_) && !comp_(key, *it)) { 494 | return it; 495 | } 496 | 497 | return end(storage_); 498 | } 499 | 500 | const_iterator find(const Key &key) const { 501 | using std::begin; 502 | using std::end; 503 | auto it = std::lower_bound(begin(storage_), end(storage_), key, comp_); 504 | if (it != end(storage_) && !comp_(key, *it)) { 505 | return it; 506 | } 507 | 508 | return end(storage_); 509 | } 510 | 511 | template 512 | iterator find(const K &key) { 513 | using std::begin; 514 | using std::end; 515 | auto it = std::lower_bound(begin(storage_), end(storage_), key, comp_); 516 | if (it != end(storage_) && !comp_(key, *it)) { 517 | return it; 518 | } 519 | 520 | return end(storage_); 521 | } 522 | 523 | template 524 | const_iterator find(const K &key) const { 525 | using std::begin; 526 | using std::end; 527 | auto it = std::lower_bound(begin(storage_), end(storage_), key, comp_); 528 | if (it != end(storage_) && !comp_(key, *it)) { 529 | return it; 530 | } 531 | 532 | return end(storage_); 533 | } 534 | 535 | public: 536 | std::pair equal_range(const Key &key) { 537 | using std::begin; 538 | using std::end; 539 | return std::equal_range(begin(storage_), end(storage_), key, comp_); 540 | } 541 | 542 | std::pair equal_range(const Key &key) const { 543 | using std::begin; 544 | using std::end; 545 | return std::equal_range(begin(storage_), end(storage_), key, comp_); 546 | } 547 | 548 | template 549 | std::pair equal_range(const K &key) { 550 | using std::begin; 551 | using std::end; 552 | return std::equal_range(begin(storage_), end(storage_), key, comp_); 553 | } 554 | 555 | template 556 | std::pair equal_range(const K &key) const { 557 | using std::begin; 558 | using std::end; 559 | return std::equal_range(begin(storage_), end(storage_), key, comp_); 560 | } 561 | 562 | public: 563 | iterator lower_bound(const Key &key) { 564 | using std::begin; 565 | using std::end; 566 | return std::lower_bound(begin(storage_), end(storage_), key, comp_); 567 | } 568 | 569 | const_iterator lower_bound(const Key &key) const { 570 | using std::begin; 571 | using std::end; 572 | return std::lower_bound(begin(storage_), end(storage_), key, comp_); 573 | } 574 | 575 | template 576 | iterator lower_bound(const K &key) { 577 | using std::begin; 578 | using std::end; 579 | return std::lower_bound(begin(storage_), end(storage_), key, comp_); 580 | } 581 | 582 | template 583 | const_iterator lower_bound(const K &key) const { 584 | using std::begin; 585 | using std::end; 586 | return std::lower_bound(begin(storage_), end(storage_), key, comp_); 587 | } 588 | 589 | public: 590 | iterator upper_bound(const Key &key) { 591 | using std::begin; 592 | using std::end; 593 | return std::upper_bound(begin(storage_), end(storage_), key, comp_); 594 | } 595 | 596 | const_iterator upper_bound(const Key &key) const { 597 | using std::begin; 598 | using std::end; 599 | return std::upper_bound(begin(storage_), end(storage_), key, comp_); 600 | } 601 | 602 | template 603 | iterator upper_bound(const K &key) { 604 | using std::begin; 605 | using std::end; 606 | return std::upper_bound(begin(storage_), end(storage_), key, comp_); 607 | } 608 | 609 | template 610 | const_iterator upper_bound(const K &key) const { 611 | using std::begin; 612 | using std::end; 613 | return std::upper_bound(begin(storage_), end(storage_), key, comp_); 614 | } 615 | 616 | public: 617 | key_compare key_comp() const { 618 | return comp_.comp; 619 | } 620 | 621 | value_compare value_comp() const { 622 | return comp_; 623 | } 624 | 625 | private: 626 | value_compare comp_; 627 | storage_type storage_; 628 | }; 629 | 630 | #endif 631 | -------------------------------------------------------------------------------- /container/include/cpp-utilities/flat_set.h: -------------------------------------------------------------------------------- 1 | #ifndef FLAT_SET_H_ 2 | #define FLAT_SET_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | template , class Allocator = std::allocator> 13 | class flat_set { 14 | public: 15 | using key_type = Key; 16 | using value_type = Key; 17 | using storage_type = std::vector; 18 | using size_type = typename storage_type::size_type; 19 | using difference_type = typename storage_type::difference_type; 20 | using key_compare = Compare; 21 | using allocator_type = Allocator; 22 | using reference = typename storage_type::reference; 23 | using const_reference = typename storage_type::const_reference; 24 | using pointer = typename storage_type::pointer; 25 | using const_pointer = typename storage_type::const_pointer; 26 | using iterator = typename storage_type::iterator; 27 | using const_iterator = typename storage_type::const_iterator; 28 | using reverse_iterator = typename storage_type::reverse_iterator; 29 | using const_reverse_iterator = typename storage_type::const_reverse_iterator; 30 | 31 | public: 32 | class value_compare { 33 | friend class flat_set; 34 | 35 | protected: 36 | value_compare(Compare c) 37 | : comp(c) { 38 | } 39 | 40 | public: 41 | bool operator()(const value_type &lhs, const value_type &rhs) const { 42 | return comp(lhs, rhs); 43 | } 44 | 45 | protected: 46 | Compare comp; 47 | }; 48 | 49 | public: 50 | flat_set() 51 | : flat_set(Compare()) { 52 | } 53 | 54 | explicit flat_set(const Compare &comp, const Allocator &alloc = Allocator()) 55 | : comp_(comp), storage_(alloc) { 56 | } 57 | 58 | template 59 | flat_set(In first, In last, const Compare &comp = Compare(), const Allocator &alloc = Allocator()) 60 | : comp_(comp), storage_(alloc) { 61 | insert(first, last); 62 | } 63 | 64 | template 65 | flat_set(In first, In last, const Allocator &alloc) 66 | : storage_(alloc) { 67 | insert(first, last); 68 | } 69 | 70 | flat_set(const flat_set &other) 71 | : comp_(other.comp_), storage_(other.storage_) { 72 | } 73 | 74 | flat_set(const flat_set &other, const Allocator &alloc) 75 | : comp_(other.comp_), storage_(other.storage_, alloc) { 76 | } 77 | 78 | flat_set(const flat_set &&other) 79 | : comp_(std::move(other.comp_)), storage_(std::move(other.storage_)) { 80 | } 81 | 82 | flat_set(const flat_set &&other, const Allocator &alloc) 83 | : comp_(std::move(other.comp_)), storage_(std::move(other.storage_), alloc) { 84 | } 85 | 86 | flat_set(std::initializer_list init, const Compare &comp = Compare(), const Allocator &alloc = Allocator()) 87 | : comp_(comp), storage_(alloc) { 88 | insert(init); 89 | } 90 | 91 | flat_set(std::initializer_list init, const Allocator &alloc) 92 | : comp_(Compare()), storage_(alloc) { 93 | insert(init); 94 | } 95 | 96 | ~flat_set() = default; 97 | 98 | public: 99 | flat_set &operator=(const flat_set &other) { 100 | if (this != &other) { 101 | flat_set(other).swap(*this); 102 | } 103 | return *this; 104 | } 105 | 106 | flat_set &operator=(flat_set &&other) { 107 | if (this != &other) { 108 | flat_set(std::move(other)).swap(*this); 109 | } 110 | return *this; 111 | } 112 | 113 | flat_set &operator=(std::initializer_list ilist) { 114 | flat_set(ilist).swap(*this); 115 | return *this; 116 | } 117 | 118 | public: 119 | allocator_type get_allocator() const { 120 | return storage_.get_allocator(); 121 | } 122 | 123 | public: 124 | iterator begin() { 125 | using std::begin; 126 | return begin(storage_); 127 | } 128 | 129 | const_iterator begin() const { 130 | using std::begin; 131 | return begin(storage_); 132 | } 133 | 134 | const_iterator cbegin() const { 135 | using std::cbegin; 136 | return cbegin(storage_); 137 | } 138 | 139 | iterator end() { 140 | using std::end; 141 | return end(storage_); 142 | } 143 | 144 | const_iterator end() const { 145 | using std::end; 146 | return end(storage_); 147 | } 148 | 149 | const_iterator cend() const { 150 | using std::cend; 151 | return cend(storage_); 152 | } 153 | 154 | reverse_iterator rbegin() { 155 | using std::rbegin; 156 | return rbegin(storage_); 157 | } 158 | 159 | const_reverse_iterator rbegin() const { 160 | using std::rbegin; 161 | return rbegin(storage_); 162 | } 163 | 164 | const_reverse_iterator crbegin() const { 165 | using std::crbegin; 166 | return crbegin(storage_); 167 | } 168 | 169 | reverse_iterator rend() { 170 | using std::rend; 171 | return rend(storage_); 172 | } 173 | 174 | const_reverse_iterator rend() const { 175 | using std::rend; 176 | return rend(storage_); 177 | } 178 | 179 | const_reverse_iterator crend() const { 180 | using std::crend; 181 | return crend(storage_); 182 | } 183 | 184 | public: 185 | bool empty() const { 186 | return storage_.empty(); 187 | } 188 | 189 | size_type size() const { 190 | return storage_.size(); 191 | } 192 | 193 | size_type max_size() const { 194 | return storage_.max_size(); 195 | } 196 | 197 | public: 198 | void clear() { 199 | storage_.clear(); 200 | } 201 | 202 | public: 203 | std::pair insert(const value_type &value) { 204 | using std::begin; 205 | using std::end; 206 | auto it = std::lower_bound(begin(storage_), end(storage_), value, comp_); 207 | if (it != end(storage_) && !comp_(value, *it)) { 208 | // found! 209 | return std::make_pair(it, false); 210 | } else { 211 | // create it 212 | auto n = storage_.insert(it, value); 213 | return std::make_pair(n, true); 214 | } 215 | } 216 | 217 | template 218 | std::pair insert(P &&value) { 219 | using std::begin; 220 | using std::end; 221 | auto it = std::lower_bound(begin(storage_), end(storage_), value, comp_); 222 | if (it != end(storage_) && !comp_(value, *it)) { 223 | // found! 224 | return std::make_pair(it, false); 225 | } else { 226 | // create it 227 | auto n = storage_.insert(it, std::forward

(value)); 228 | return std::make_pair(n, true); 229 | } 230 | } 231 | 232 | std::pair insert(value_type &&value) { 233 | using std::begin; 234 | using std::end; 235 | auto it = std::lower_bound(begin(storage_), end(storage_), value, comp_); 236 | if (it != end(storage_) && !comp_(value, *it)) { 237 | // found! 238 | return std::make_pair(it, false); 239 | } else { 240 | // create it 241 | auto n = storage_.insert(it, std::move(value)); 242 | return std::make_pair(n, true); 243 | } 244 | } 245 | 246 | std::pair insert(const_iterator hint, const value_type &value) { 247 | (void)hint; 248 | return insert(value); 249 | } 250 | 251 | template 252 | std::pair insert(const_iterator hint, P &&value) { 253 | (void)hint; 254 | return insert(std::forward

(value)); 255 | } 256 | 257 | std::pair insert(const_iterator hint, value_type &&value) { 258 | (void)hint; 259 | return insert(std::move(value)); 260 | } 261 | 262 | template 263 | void insert(In first, In last) { 264 | while (first != last) { 265 | insert(*first++); 266 | } 267 | } 268 | 269 | void insert(std::initializer_list ilist) { 270 | for (auto &&value : ilist) { 271 | insert(std::move(value)); 272 | } 273 | } 274 | 275 | public: 276 | template 277 | std::pair insert_or_assign(const key_type &key, M &&obj) { 278 | auto it = insert(key); 279 | if (it.second) { 280 | it.first->second = std::forward(obj); 281 | } 282 | return it; 283 | } 284 | 285 | template 286 | std::pair insert_or_assign(key_type &&key, M &&obj) { 287 | auto it = insert(std::move(key)); 288 | if (it.second) { 289 | it.first->second = std::forward(obj); 290 | } 291 | return it; 292 | } 293 | 294 | template 295 | std::pair insert_or_assign(const_iterator hint, const key_type &key, M &&obj) { 296 | auto it = insert(hint, key); 297 | if (it.second) { 298 | it.first->second = std::forward(obj); 299 | } 300 | return it; 301 | } 302 | 303 | template 304 | std::pair insert_or_assign(const_iterator hint, key_type &&key, M &&obj) { 305 | auto it = insert(hint, key); 306 | if (it.second) { 307 | it.first->second = std::forward(obj); 308 | } 309 | return it; 310 | } 311 | 312 | public: 313 | // TODO: try_emplace 314 | template 315 | std::pair emplace(Args &&...args) { 316 | using std::begin; 317 | using std::end; 318 | 319 | value_type temp(std::forward(args)...); 320 | 321 | auto it = std::lower_bound(begin(storage_), end(storage_), temp, comp_); 322 | if (it != end(storage_) && !comp_(temp, *it)) { 323 | // found! 324 | return std::make_pair(it, false); 325 | } else { 326 | // create it 327 | auto n = storage_.emplace(it, std::move(temp)); 328 | return std::make_pair(n, true); 329 | } 330 | } 331 | 332 | template 333 | std::pair emplace_hint(const_iterator hint, Args &&...args) { 334 | 335 | (void)hint; 336 | 337 | using std::begin; 338 | using std::end; 339 | 340 | value_type temp(std::forward(args)...); 341 | 342 | auto it = std::lower_bound(begin(storage_), end(storage_), temp, comp_); 343 | if (it != end(storage_) && !comp_(temp, *it)) { 344 | // found! 345 | return std::make_pair(it, false); 346 | } else { 347 | // create it 348 | auto n = storage_.emplace(it, std::move(temp)); 349 | return std::make_pair(n, true); 350 | } 351 | } 352 | 353 | public: 354 | iterator erase(const_iterator pos) { 355 | return storage_.erase(pos); 356 | } 357 | 358 | iterator erase(iterator pos) { 359 | return storage_.erase(pos); 360 | } 361 | 362 | iterator erase(const_iterator first, const_iterator last) { 363 | return storage_.erase(first, last); 364 | } 365 | 366 | size_type erase(const key_type &key) { 367 | size_type c = 0; 368 | auto r = equal_range(key); 369 | auto first = r.first; 370 | auto second = r.second; 371 | while (first != second) { 372 | erase(first++); 373 | ++c; 374 | } 375 | return c; 376 | } 377 | 378 | public: 379 | void swap(flat_set &other) { 380 | using std::swap; 381 | swap(comp_, other.comp_); 382 | swap(storage_, other.storage_); 383 | } 384 | 385 | public: 386 | size_type count(const Key &key) const { 387 | size_type c = 0; 388 | using std::begin; 389 | using std::end; 390 | 391 | auto it = std::lower_bound(begin(storage_), end(storage_), key, comp_); 392 | while (it != end(storage_) && !comp_(key, *it)) { 393 | ++c; 394 | } 395 | 396 | return c; 397 | } 398 | 399 | template 400 | size_type count(const K &key) const { 401 | size_type c = 0; 402 | using std::begin; 403 | using std::end; 404 | auto it = std::lower_bound(begin(storage_), end(storage_), key, comp_); 405 | while (it != end(storage_) && !comp_(key, *it)) { 406 | ++c; 407 | } 408 | 409 | return c; 410 | } 411 | 412 | public: 413 | iterator find(const Key &key) { 414 | using std::begin; 415 | using std::end; 416 | auto it = std::lower_bound(begin(storage_), end(storage_), key, comp_); 417 | if (it != end(storage_) && !comp_(key, *it)) { 418 | return it; 419 | } 420 | 421 | return end(storage_); 422 | } 423 | 424 | const_iterator find(const Key &key) const { 425 | using std::begin; 426 | using std::end; 427 | auto it = std::lower_bound(begin(storage_), end(storage_), key, comp_); 428 | if (it != end(storage_) && !comp_(key, *it)) { 429 | return it; 430 | } 431 | 432 | return end(storage_); 433 | } 434 | 435 | template 436 | iterator find(const K &key) { 437 | using std::begin; 438 | using std::end; 439 | auto it = std::lower_bound(begin(storage_), end(storage_), key, comp_); 440 | if (it != end(storage_) && !comp_(key, *it)) { 441 | return it; 442 | } 443 | 444 | return end(storage_); 445 | } 446 | 447 | template 448 | const_iterator find(const K &key) const { 449 | using std::begin; 450 | using std::end; 451 | auto it = std::lower_bound(begin(storage_), end(storage_), key, comp_); 452 | if (it != end(storage_) && !comp_(key, *it)) { 453 | return it; 454 | } 455 | 456 | return end(storage_); 457 | } 458 | 459 | public: 460 | std::pair equal_range(const Key &key) { 461 | using std::begin; 462 | using std::end; 463 | return std::equal_range(begin(storage_), end(storage_), key, comp_); 464 | } 465 | 466 | std::pair equal_range(const Key &key) const { 467 | using std::begin; 468 | using std::end; 469 | return std::equal_range(begin(storage_), end(storage_), key, comp_); 470 | } 471 | 472 | template 473 | std::pair equal_range(const K &key) { 474 | using std::begin; 475 | using std::end; 476 | return std::equal_range(begin(storage_), end(storage_), key, comp_); 477 | } 478 | 479 | template 480 | std::pair equal_range(const K &key) const { 481 | using std::begin; 482 | using std::end; 483 | return std::equal_range(begin(storage_), end(storage_), key, comp_); 484 | } 485 | 486 | public: 487 | iterator lower_bound(const Key &key) { 488 | using std::begin; 489 | using std::end; 490 | return std::lower_bound(begin(storage_), end(storage_), key, comp_); 491 | } 492 | 493 | const_iterator lower_bound(const Key &key) const { 494 | using std::begin; 495 | using std::end; 496 | return std::lower_bound(begin(storage_), end(storage_), key, comp_); 497 | } 498 | 499 | template 500 | iterator lower_bound(const K &key) { 501 | using std::begin; 502 | using std::end; 503 | return std::lower_bound(begin(storage_), end(storage_), key, comp_); 504 | } 505 | 506 | template 507 | const_iterator lower_bound(const K &key) const { 508 | using std::begin; 509 | using std::end; 510 | return std::lower_bound(begin(storage_), end(storage_), key, comp_); 511 | } 512 | 513 | public: 514 | iterator upper_bound(const Key &key) { 515 | using std::begin; 516 | using std::end; 517 | return std::upper_bound(begin(storage_), end(storage_), key, comp_); 518 | } 519 | 520 | const_iterator upper_bound(const Key &key) const { 521 | using std::begin; 522 | using std::end; 523 | return std::upper_bound(begin(storage_), end(storage_), key, comp_); 524 | } 525 | 526 | template 527 | iterator upper_bound(const K &key) { 528 | using std::begin; 529 | using std::end; 530 | return std::upper_bound(begin(storage_), end(storage_), key, comp_); 531 | } 532 | 533 | template 534 | const_iterator upper_bound(const K &key) const { 535 | using std::begin; 536 | using std::end; 537 | return std::upper_bound(begin(storage_), end(storage_), key, comp_); 538 | } 539 | 540 | public: 541 | key_compare key_comp() const { 542 | return comp_.comp; 543 | } 544 | 545 | value_compare value_comp() const { 546 | return comp_; 547 | } 548 | 549 | private: 550 | value_compare comp_; 551 | storage_type storage_; 552 | }; 553 | 554 | #endif 555 | -------------------------------------------------------------------------------- /container/include/cpp-utilities/lru_cache.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef LRU_CACHE_H_ 3 | #define LRU_CACHE_H_ 4 | 5 | #if __cplusplus < 201703L 6 | #error "This header requires C++17 or greater" 7 | #endif 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | template 18 | class lru_cache { 19 | public: 20 | using value_type = std::pair; 21 | using list_type = std::list; 22 | using list_iterator = typename list_type::iterator; 23 | using map_type = std::unordered_map; 24 | using map_iterator = typename map_type::iterator; 25 | 26 | public: 27 | lru_cache(size_t capacity) 28 | : capacity_(capacity) {} 29 | 30 | ~lru_cache() { 31 | clear(); 32 | } 33 | 34 | size_t size() const { 35 | return size_; 36 | } 37 | 38 | size_t capacity() const { 39 | return capacity_; 40 | } 41 | 42 | void clear() { 43 | std::unique_lock lock(mutex_); 44 | list_.clear(); 45 | index_.clear(); 46 | }; 47 | 48 | bool exists(const Key &key) const { 49 | std::shared_lock lock(mutex_); 50 | return index_.find(key) != index_.end(); 51 | } 52 | 53 | void remove(const Key &key) { 54 | std::unique_lock lock(mutex_); 55 | auto it = index_.find(key); 56 | if (it != index_.end()) { 57 | remove_entry(it); 58 | } 59 | } 60 | 61 | void touch(const Key &key) { 62 | std::unique_lock lock(mutex_); 63 | touch_entry(key); 64 | } 65 | 66 | std::optional fetch(const Key &key, bool touch_data = true) { 67 | std::unique_lock lock(mutex_); 68 | auto it = index_.find(key); 69 | if (it == index_.end()) { 70 | return {}; 71 | } 72 | 73 | T tmp = it->second->second; 74 | if (touch_data) { 75 | touch_entry(it); 76 | } 77 | 78 | return tmp; 79 | } 80 | 81 | std::optional take(const Key &key) { 82 | std::unique_lock lock(mutex_); 83 | auto it = index_.find(key); 84 | if (it == index_.end()) { 85 | return {}; 86 | } 87 | 88 | T tmp = std::move(it->second->second); 89 | remove_entry(it); 90 | return tmp; 91 | } 92 | 93 | template 94 | void insert(const Key &key, U &&data) { 95 | std::unique_lock lock(mutex_); 96 | auto it = touch_entry(key); 97 | if (it != index_.end()) { 98 | remove_entry(it); 99 | } 100 | 101 | list_.emplace_front(key, std::forward(data)); 102 | index_.emplace(key, list_.begin()); 103 | ++size_; 104 | 105 | flush_overflow(); 106 | } 107 | 108 | std::vector keys() const { 109 | 110 | std::vector ret; 111 | ret.reserve(list_.size()); 112 | { 113 | std::shared_lock lock(mutex_); 114 | std::transform(list_.begin(), list_.end(), std::back_inserter(ret), [](const value_type &entry) { 115 | return entry.first; 116 | }); 117 | } 118 | 119 | return ret; 120 | } 121 | 122 | private: 123 | void flush_overflow() { 124 | while (size_ > capacity_) { 125 | remove_entry(list_.back().first); 126 | } 127 | } 128 | 129 | map_iterator touch_entry(const Key &key) { 130 | auto it = index_.find(key); 131 | if (it == index_.end()) { 132 | return it; 133 | } 134 | 135 | return touch_entry(it); 136 | } 137 | 138 | template 139 | Iter touch_entry(Iter it) { 140 | list_.splice(list_.begin(), list_, it->second); 141 | return it; 142 | } 143 | 144 | template 145 | void remove_entry(Iter it) { 146 | --size_; 147 | list_.erase(it->second); 148 | index_.erase(it); 149 | } 150 | 151 | void remove_entry(const Key &key) { 152 | auto it = index_.find(key); 153 | if (it != index_.end()) { 154 | remove_entry(it); 155 | } 156 | } 157 | 158 | private: 159 | list_type list_; 160 | map_type index_; 161 | size_t capacity_ = 0; 162 | size_t size_ = 0; 163 | 164 | private: 165 | mutable std::shared_mutex mutex_; 166 | }; 167 | 168 | #endif 169 | -------------------------------------------------------------------------------- /container/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | add_subdirectory(flat_map) 3 | add_subdirectory(flat_set) 4 | add_subdirectory(lru_cache) 5 | -------------------------------------------------------------------------------- /container/test/flat_map/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | add_executable(cpp-utilities-container-test-flat_map 4 | flat_map.cpp 5 | ) 6 | 7 | target_link_libraries(cpp-utilities-container-test-flat_map 8 | PRIVATE 9 | cpp-utilities::container 10 | cpp-utilities::defaults 11 | ) 12 | 13 | add_test( 14 | NAME cpp-utilities-container-test-flat_map 15 | COMMAND $ 16 | ) 17 | 18 | -------------------------------------------------------------------------------- /container/test/flat_map/flat_map.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | template class flat_map; 6 | 7 | int main() { 8 | flat_map map = { 9 | std::make_pair(1, "one"), 10 | std::make_pair(2, "two"), 11 | }; 12 | 13 | map[10] = "ten"; 14 | map[5] = "five"; 15 | map[50] = "fifty"; 16 | map[50] = "fifty!"; 17 | 18 | std::cout << map.at(10) << std::endl; 19 | 20 | map.emplace(100, "one hundred"); 21 | map.insert(std::make_pair(1000, "one thousand")); 22 | 23 | 24 | for(auto &&x : map) { 25 | //x.first = 1000; 26 | std::cout << x.first << " : " << x.second << "\n"; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /container/test/flat_set/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | add_executable(cpp-utilities-container-test-flat_set 4 | flat_set.cpp 5 | ) 6 | 7 | target_link_libraries(cpp-utilities-container-test-flat_set 8 | PRIVATE 9 | cpp-utilities::container 10 | cpp-utilities::defaults 11 | ) 12 | 13 | add_test( 14 | NAME cpp-utilities-container-test-flat_set 15 | COMMAND $ 16 | ) 17 | -------------------------------------------------------------------------------- /container/test/flat_set/flat_set.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | template class flat_set; 6 | 7 | int main() { 8 | 9 | flat_set set = { 10 | 10, 2, 50 11 | }; 12 | 13 | 14 | set.insert(1000); 15 | set.insert(1000); 16 | 17 | 18 | for(auto &&x : set) { 19 | std::cout << x << "\n"; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /container/test/lru_cache/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | find_package(Threads REQUIRED) 4 | 5 | add_executable(cpp-utilities-container-test-lru_cache 6 | lru_cache.cpp 7 | ) 8 | 9 | target_link_libraries(cpp-utilities-container-test-lru_cache 10 | PRIVATE 11 | cpp-utilities::container 12 | cpp-utilities::defaults 13 | Threads::Threads 14 | ) 15 | 16 | add_test( 17 | NAME cpp-utilities-container-test-lru_cache 18 | COMMAND $ 19 | ) 20 | 21 | set_property(TARGET cpp-utilities-container-test-lru_cache PROPERTY CXX_STANDARD 17) 22 | -------------------------------------------------------------------------------- /container/test/lru_cache/lru_cache.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main() { 8 | 9 | lru_cache> cache(10); 10 | uint64_t address = 1024 * 1024 * 50; 11 | 12 | for (int i = 0; i < 20; ++i) { 13 | cache.insert(address + i, std::make_unique(4096)); 14 | } 15 | 16 | std::cout << "CACHE SIZE: " << cache.size() << std::endl; 17 | 18 | if (auto p = cache.take(address)) { 19 | std::cout << "Address: " << (void *)p->get() << std::endl; 20 | } 21 | 22 | if (auto p = cache.take(address + 4)) { 23 | std::cout << "Address: " << (void *)p->get() << std::endl; 24 | } 25 | 26 | if (auto p = cache.take(address + 9)) { 27 | std::cout << "Address: " << (void *)p->get() << std::endl; 28 | } 29 | 30 | for (auto key : cache.keys()) { 31 | std::cout << "KEY: " << key << std::endl; 32 | } 33 | 34 | std::cout << "CACHE SIZE: " << cache.size() << std::endl; 35 | } 36 | -------------------------------------------------------------------------------- /defaults/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | add_library(cpp-utilities-defaults INTERFACE) 4 | add_library(cpp-utilities::defaults ALIAS cpp-utilities-defaults) 5 | 6 | target_compile_options(cpp-utilities-defaults 7 | INTERFACE 8 | -Werror 9 | ) 10 | 11 | -------------------------------------------------------------------------------- /fixed/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | add_library(cpp-utilities-fixed INTERFACE) 4 | add_library(cpp-utilities::fixed ALIAS cpp-utilities-fixed) 5 | 6 | target_include_directories(cpp-utilities-fixed 7 | INTERFACE 8 | ${CMAKE_CURRENT_LIST_DIR}/include 9 | ) 10 | 11 | add_subdirectory(test) 12 | 13 | -------------------------------------------------------------------------------- /fixed/include/cpp-utilities/fixed.h: -------------------------------------------------------------------------------- 1 | // From: https://github.com/eteran/cpp-utilities/blob/master/fixed/include/cpp-utilities/fixed.h 2 | // See also: http://stackoverflow.com/questions/79677/whats-the-best-way-to-do-fixed-point-math 3 | /* 4 | * The MIT License (MIT) 5 | * 6 | * Copyright (c) 2015 Evan Teran 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in all 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | #ifndef FIXED_H_ 28 | #define FIXED_H_ 29 | 30 | #if __cplusplus >= 201402L 31 | #define CONSTEXPR14 constexpr 32 | #else 33 | #define CONSTEXPR14 34 | #endif 35 | 36 | #include // for size_t 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | namespace numeric { 43 | 44 | template 45 | class fixed; 46 | 47 | namespace detail { 48 | 49 | // helper templates to make magic with types :) 50 | // these allow us to determine reasonable types from 51 | // a desired size, they also let us infer the next largest type 52 | // from a type which is nice for the division op 53 | template 54 | struct type_from_size { 55 | using value_type = void; 56 | using unsigned_type = void; 57 | using signed_type = void; 58 | static constexpr bool is_specialized = false; 59 | }; 60 | 61 | #if defined(__GNUC__) && defined(__x86_64__) && !defined(__STRICT_ANSI__) 62 | template <> 63 | struct type_from_size<128> { 64 | static constexpr bool is_specialized = true; 65 | static constexpr size_t size = 128; 66 | 67 | using value_type = __int128; 68 | using unsigned_type = unsigned __int128; 69 | using signed_type = __int128; 70 | using next_size = type_from_size<256>; 71 | }; 72 | #endif 73 | 74 | template <> 75 | struct type_from_size<64> { 76 | static constexpr bool is_specialized = true; 77 | static constexpr size_t size = 64; 78 | 79 | using value_type = int64_t; 80 | using unsigned_type = std::make_unsigned::type; 81 | using signed_type = std::make_signed::type; 82 | using next_size = type_from_size<128>; 83 | }; 84 | 85 | template <> 86 | struct type_from_size<32> { 87 | static constexpr bool is_specialized = true; 88 | static constexpr size_t size = 32; 89 | 90 | using value_type = int32_t; 91 | using unsigned_type = std::make_unsigned::type; 92 | using signed_type = std::make_signed::type; 93 | using next_size = type_from_size<64>; 94 | }; 95 | 96 | template <> 97 | struct type_from_size<16> { 98 | static constexpr bool is_specialized = true; 99 | static constexpr size_t size = 16; 100 | 101 | using value_type = int16_t; 102 | using unsigned_type = std::make_unsigned::type; 103 | using signed_type = std::make_signed::type; 104 | using next_size = type_from_size<32>; 105 | }; 106 | 107 | template <> 108 | struct type_from_size<8> { 109 | static constexpr bool is_specialized = true; 110 | static constexpr size_t size = 8; 111 | 112 | using value_type = int8_t; 113 | using unsigned_type = std::make_unsigned::type; 114 | using signed_type = std::make_signed::type; 115 | using next_size = type_from_size<16>; 116 | }; 117 | 118 | // this is to assist in adding support for non-native base 119 | // types (for adding big-int support), this should be fine 120 | // unless your bit-int class doesn't nicely support casting 121 | template 122 | constexpr B next_to_base(N rhs) { 123 | return static_cast(rhs); 124 | } 125 | 126 | struct divide_by_zero : std::exception { 127 | }; 128 | 129 | template 130 | CONSTEXPR14 fixed divide(fixed numerator, fixed denominator, fixed &remainder, typename std::enable_if::next_size::is_specialized>::type * = nullptr) { 131 | 132 | using next_type = typename fixed::next_type; 133 | using base_type = typename fixed::base_type; 134 | constexpr size_t fractional_bits = fixed::fractional_bits; 135 | 136 | next_type t(numerator.to_raw()); 137 | t <<= fractional_bits; 138 | 139 | fixed quotient; 140 | 141 | quotient = fixed::from_base(next_to_base(t / denominator.to_raw())); 142 | remainder = fixed::from_base(next_to_base(t % denominator.to_raw())); 143 | 144 | return quotient; 145 | } 146 | 147 | template 148 | CONSTEXPR14 fixed divide(fixed numerator, fixed denominator, fixed &remainder, typename std::enable_if::next_size::is_specialized>::type * = nullptr) { 149 | 150 | using unsigned_type = typename fixed::unsigned_type; 151 | 152 | constexpr int bits = fixed::total_bits; 153 | 154 | if (denominator == 0) { 155 | throw divide_by_zero(); 156 | } else { 157 | 158 | int sign = 0; 159 | 160 | fixed quotient; 161 | 162 | if (numerator < 0) { 163 | sign ^= 1; 164 | numerator = -numerator; 165 | } 166 | 167 | if (denominator < 0) { 168 | sign ^= 1; 169 | denominator = -denominator; 170 | } 171 | 172 | unsigned_type n = numerator.to_raw(); 173 | unsigned_type d = denominator.to_raw(); 174 | unsigned_type x = 1; 175 | unsigned_type answer = 0; 176 | 177 | // egyptian division algorithm 178 | while ((n >= d) && (((d >> (bits - 1)) & 1) == 0)) { 179 | x <<= 1; 180 | d <<= 1; 181 | } 182 | 183 | while (x != 0) { 184 | if (n >= d) { 185 | n -= d; 186 | answer += x; 187 | } 188 | 189 | x >>= 1; 190 | d >>= 1; 191 | } 192 | 193 | unsigned_type l1 = n; 194 | unsigned_type l2 = denominator.to_raw(); 195 | 196 | // calculate the lower bits (needs to be unsigned) 197 | while (l1 >> (bits - F) > 0) { 198 | l1 >>= 1; 199 | l2 >>= 1; 200 | } 201 | const unsigned_type lo = (l1 << F) / l2; 202 | 203 | quotient = fixed::from_base((answer << F) | lo); 204 | remainder = n; 205 | 206 | if (sign) { 207 | quotient = -quotient; 208 | } 209 | 210 | return quotient; 211 | } 212 | } 213 | 214 | // this is the usual implementation of multiplication 215 | template 216 | CONSTEXPR14 fixed multiply(fixed lhs, fixed rhs, typename std::enable_if::next_size::is_specialized>::type * = nullptr) { 217 | 218 | using next_type = typename fixed::next_type; 219 | using base_type = typename fixed::base_type; 220 | 221 | constexpr size_t fractional_bits = fixed::fractional_bits; 222 | 223 | next_type t(static_cast(lhs.to_raw()) * static_cast(rhs.to_raw())); 224 | t >>= fractional_bits; 225 | 226 | return fixed::from_base(next_to_base(t)); 227 | } 228 | 229 | // this is the fall back version we use when we don't have a next size 230 | // it is slightly slower, but is more robust since it doesn't 231 | // require and upgraded type 232 | template 233 | CONSTEXPR14 fixed multiply(fixed lhs, fixed rhs, typename std::enable_if::next_size::is_specialized>::type * = nullptr) { 234 | 235 | using base_type = typename fixed::base_type; 236 | 237 | constexpr size_t fractional_bits = fixed::fractional_bits; 238 | constexpr base_type integer_mask = fixed::integer_mask; 239 | constexpr base_type fractional_mask = fixed::fractional_mask; 240 | 241 | // more costly but doesn't need a larger type 242 | const base_type a_hi = (lhs.to_raw() & integer_mask) >> fractional_bits; 243 | const base_type b_hi = (rhs.to_raw() & integer_mask) >> fractional_bits; 244 | const base_type a_lo = (lhs.to_raw() & fractional_mask); 245 | const base_type b_lo = (rhs.to_raw() & fractional_mask); 246 | 247 | const base_type x1 = a_hi * b_hi; 248 | const base_type x2 = a_hi * b_lo; 249 | const base_type x3 = a_lo * b_hi; 250 | const base_type x4 = a_lo * b_lo; 251 | 252 | return fixed::from_base((x1 << fractional_bits) + (x3 + x2) + (x4 >> fractional_bits)); 253 | } 254 | } 255 | 256 | template 257 | class fixed { 258 | static_assert(detail::type_from_size::is_specialized, "invalid combination of sizes"); 259 | 260 | public: 261 | static constexpr size_t fractional_bits = F; 262 | static constexpr size_t integer_bits = I; 263 | static constexpr size_t total_bits = I + F; 264 | 265 | using base_type_info = detail::type_from_size; 266 | 267 | using base_type = typename base_type_info::value_type; 268 | using next_type = typename base_type_info::next_size::value_type; 269 | using unsigned_type = typename base_type_info::unsigned_type; 270 | 271 | public: 272 | #ifdef __GNUC__ 273 | #pragma GCC diagnostic push 274 | #pragma GCC diagnostic ignored "-Woverflow" 275 | #endif 276 | static constexpr base_type fractional_mask = ~(static_cast(~base_type(0)) << fractional_bits); 277 | static constexpr base_type integer_mask = ~fractional_mask; 278 | #ifdef __GNUC__ 279 | #pragma GCC diagnostic pop 280 | #endif 281 | 282 | public: 283 | static constexpr base_type one = base_type(1) << fractional_bits; 284 | 285 | public: // constructors 286 | fixed() = default; 287 | fixed(const fixed &) = default; 288 | fixed &operator=(const fixed &) = default; 289 | 290 | template 291 | constexpr fixed(Number n, typename std::enable_if::value>::type * = nullptr) 292 | : data_(static_cast(n * one)) { 293 | } 294 | 295 | public: // conversion 296 | template 297 | CONSTEXPR14 explicit fixed(fixed other) { 298 | static_assert(I2 <= I && F2 <= F, "Scaling conversion can only upgrade types"); 299 | using T = fixed; 300 | 301 | const base_type fractional = (other.data_ & T::fractional_mask); 302 | const base_type integer = (other.data_ & T::integer_mask) >> T::fractional_bits; 303 | data_ = (integer << fractional_bits) | (fractional << (fractional_bits - T::fractional_bits)); 304 | } 305 | 306 | private: 307 | // this makes it simpler to create a fixed point object from 308 | // a native type without scaling 309 | // use "fixed::from_base" in order to perform this. 310 | struct NoScale {}; 311 | 312 | constexpr fixed(base_type n, const NoScale &) 313 | : data_(n) { 314 | } 315 | 316 | public: 317 | constexpr static fixed from_base(base_type n) { 318 | return fixed(n, NoScale()); 319 | } 320 | 321 | public: // comparison operators 322 | constexpr bool operator==(fixed rhs) const { 323 | return data_ == rhs.data_; 324 | } 325 | 326 | constexpr bool operator!=(fixed rhs) const { 327 | return data_ != rhs.data_; 328 | } 329 | 330 | constexpr bool operator<(fixed rhs) const { 331 | return data_ < rhs.data_; 332 | } 333 | 334 | constexpr bool operator>(fixed rhs) const { 335 | return data_ > rhs.data_; 336 | } 337 | 338 | constexpr bool operator<=(fixed rhs) const { 339 | return data_ <= rhs.data_; 340 | } 341 | 342 | constexpr bool operator>=(fixed rhs) const { 343 | return data_ >= rhs.data_; 344 | } 345 | 346 | public: // unary operators 347 | constexpr bool operator!() const { 348 | return !data_; 349 | } 350 | 351 | constexpr fixed operator~() const { 352 | // NOTE(eteran): this will often appear to "just negate" the value 353 | // that is not an error, it is because -x == (~x+1) 354 | // and that "+1" is adding an infinitesimally small fraction to the 355 | // complimented value 356 | return fixed::from_base(~data_); 357 | } 358 | 359 | constexpr fixed operator-() const { 360 | return fixed::from_base(-data_); 361 | } 362 | 363 | constexpr fixed operator+() const { 364 | return fixed::from_base(+data_); 365 | } 366 | 367 | CONSTEXPR14 fixed &operator++() { 368 | data_ += one; 369 | return *this; 370 | } 371 | 372 | CONSTEXPR14 fixed &operator--() { 373 | data_ -= one; 374 | return *this; 375 | } 376 | 377 | CONSTEXPR14 fixed operator++(int) { 378 | fixed tmp(*this); 379 | data_ += one; 380 | return tmp; 381 | } 382 | 383 | CONSTEXPR14 fixed operator--(int) { 384 | fixed tmp(*this); 385 | data_ -= one; 386 | return tmp; 387 | } 388 | 389 | public: // basic math operators 390 | CONSTEXPR14 fixed &operator+=(fixed n) { 391 | data_ += n.data_; 392 | return *this; 393 | } 394 | 395 | CONSTEXPR14 fixed &operator-=(fixed n) { 396 | data_ -= n.data_; 397 | return *this; 398 | } 399 | 400 | CONSTEXPR14 fixed &operator*=(fixed n) { 401 | return assign(detail::multiply(*this, n)); 402 | } 403 | 404 | CONSTEXPR14 fixed &operator/=(fixed n) { 405 | fixed temp; 406 | return assign(detail::divide(*this, n, temp)); 407 | } 408 | 409 | private: 410 | CONSTEXPR14 fixed &assign(fixed rhs) { 411 | data_ = rhs.data_; 412 | return *this; 413 | } 414 | 415 | public: // binary math operators, effects underlying bit pattern since these 416 | // don't really typically make sense for non-integer values 417 | CONSTEXPR14 fixed &operator&=(fixed n) { 418 | data_ &= n.data_; 419 | return *this; 420 | } 421 | 422 | CONSTEXPR14 fixed &operator|=(fixed n) { 423 | data_ |= n.data_; 424 | return *this; 425 | } 426 | 427 | CONSTEXPR14 fixed &operator^=(fixed n) { 428 | data_ ^= n.data_; 429 | return *this; 430 | } 431 | 432 | template ::value>::type> 433 | CONSTEXPR14 fixed &operator>>=(Integer n) { 434 | data_ >>= n; 435 | return *this; 436 | } 437 | 438 | template ::value>::type> 439 | CONSTEXPR14 fixed &operator<<=(Integer n) { 440 | data_ <<= n; 441 | return *this; 442 | } 443 | 444 | public: // conversion to basic types 445 | constexpr int to_int() const { 446 | return (data_ & integer_mask) >> fractional_bits; 447 | } 448 | 449 | constexpr unsigned int to_uint() const { 450 | return static_cast(data_ & integer_mask) >> fractional_bits; 451 | } 452 | 453 | constexpr float to_float() const { 454 | return static_cast(data_) / fixed::one; 455 | } 456 | 457 | constexpr double to_double() const { 458 | return static_cast(data_) / fixed::one; 459 | } 460 | 461 | constexpr base_type to_raw() const { 462 | return data_; 463 | } 464 | 465 | public: 466 | CONSTEXPR14 void swap(fixed &rhs) { 467 | using std::swap; 468 | swap(data_, rhs.data_); 469 | } 470 | 471 | public: 472 | base_type data_ = 0; 473 | }; 474 | 475 | // if we have the same fractional portion, but differing integer portions, we trivially upgrade the smaller type 476 | template 477 | CONSTEXPR14 typename std::conditional= I2, fixed, fixed>::type operator+(fixed lhs, fixed rhs) { 478 | 479 | using T = typename std::conditional< 480 | I1 >= I2, 481 | fixed, 482 | fixed>::type; 483 | 484 | const T l = T::from_base(lhs.to_raw()); 485 | const T r = T::from_base(rhs.to_raw()); 486 | return l + r; 487 | } 488 | 489 | template 490 | CONSTEXPR14 typename std::conditional= I2, fixed, fixed>::type operator-(fixed lhs, fixed rhs) { 491 | 492 | using T = typename std::conditional< 493 | I1 >= I2, 494 | fixed, 495 | fixed>::type; 496 | 497 | const T l = T::from_base(lhs.to_raw()); 498 | const T r = T::from_base(rhs.to_raw()); 499 | return l - r; 500 | } 501 | 502 | template 503 | CONSTEXPR14 typename std::conditional= I2, fixed, fixed>::type operator*(fixed lhs, fixed rhs) { 504 | 505 | using T = typename std::conditional< 506 | I1 >= I2, 507 | fixed, 508 | fixed>::type; 509 | 510 | const T l = T::from_base(lhs.to_raw()); 511 | const T r = T::from_base(rhs.to_raw()); 512 | return l * r; 513 | } 514 | 515 | template 516 | CONSTEXPR14 typename std::conditional= I2, fixed, fixed>::type operator/(fixed lhs, fixed rhs) { 517 | 518 | using T = typename std::conditional< 519 | I1 >= I2, 520 | fixed, 521 | fixed>::type; 522 | 523 | const T l = T::from_base(lhs.to_raw()); 524 | const T r = T::from_base(rhs.to_raw()); 525 | return l / r; 526 | } 527 | 528 | template 529 | std::ostream &operator<<(std::ostream &os, fixed f) { 530 | os << f.to_double(); 531 | return os; 532 | } 533 | 534 | // basic math operators 535 | template 536 | CONSTEXPR14 fixed operator+(fixed lhs, fixed rhs) { 537 | lhs += rhs; 538 | return lhs; 539 | } 540 | 541 | template 542 | CONSTEXPR14 fixed operator-(fixed lhs, fixed rhs) { 543 | lhs -= rhs; 544 | return lhs; 545 | } 546 | 547 | template 548 | CONSTEXPR14 fixed operator*(fixed lhs, fixed rhs) { 549 | lhs *= rhs; 550 | return lhs; 551 | } 552 | 553 | template 554 | CONSTEXPR14 fixed operator/(fixed lhs, fixed rhs) { 555 | lhs /= rhs; 556 | return lhs; 557 | } 558 | 559 | template ::value>::type> 560 | CONSTEXPR14 fixed operator+(fixed lhs, Number rhs) { 561 | lhs += fixed(rhs); 562 | return lhs; 563 | } 564 | 565 | template ::value>::type> 566 | CONSTEXPR14 fixed operator-(fixed lhs, Number rhs) { 567 | lhs -= fixed(rhs); 568 | return lhs; 569 | } 570 | 571 | template ::value>::type> 572 | CONSTEXPR14 fixed operator*(fixed lhs, Number rhs) { 573 | lhs *= fixed(rhs); 574 | return lhs; 575 | } 576 | 577 | template ::value>::type> 578 | CONSTEXPR14 fixed operator/(fixed lhs, Number rhs) { 579 | lhs /= fixed(rhs); 580 | return lhs; 581 | } 582 | 583 | template ::value>::type> 584 | CONSTEXPR14 fixed operator+(Number lhs, fixed rhs) { 585 | fixed tmp(lhs); 586 | tmp += rhs; 587 | return tmp; 588 | } 589 | 590 | template ::value>::type> 591 | CONSTEXPR14 fixed operator-(Number lhs, fixed rhs) { 592 | fixed tmp(lhs); 593 | tmp -= rhs; 594 | return tmp; 595 | } 596 | 597 | template ::value>::type> 598 | CONSTEXPR14 fixed operator*(Number lhs, fixed rhs) { 599 | fixed tmp(lhs); 600 | tmp *= rhs; 601 | return tmp; 602 | } 603 | 604 | template ::value>::type> 605 | CONSTEXPR14 fixed operator/(Number lhs, fixed rhs) { 606 | fixed tmp(lhs); 607 | tmp /= rhs; 608 | return tmp; 609 | } 610 | 611 | // shift operators 612 | template ::value>::type> 613 | CONSTEXPR14 fixed operator<<(fixed lhs, Integer rhs) { 614 | lhs <<= rhs; 615 | return lhs; 616 | } 617 | 618 | template ::value>::type> 619 | CONSTEXPR14 fixed operator>>(fixed lhs, Integer rhs) { 620 | lhs >>= rhs; 621 | return lhs; 622 | } 623 | 624 | // comparison operators 625 | template ::value>::type> 626 | constexpr bool operator>(fixed lhs, Number rhs) { 627 | return lhs > fixed(rhs); 628 | } 629 | 630 | template ::value>::type> 631 | constexpr bool operator<(fixed lhs, Number rhs) { 632 | return lhs < fixed(rhs); 633 | } 634 | 635 | template ::value>::type> 636 | constexpr bool operator>=(fixed lhs, Number rhs) { 637 | return lhs >= fixed(rhs); 638 | } 639 | 640 | template ::value>::type> 641 | constexpr bool operator<=(fixed lhs, Number rhs) { 642 | return lhs <= fixed(rhs); 643 | } 644 | 645 | template ::value>::type> 646 | constexpr bool operator==(fixed lhs, Number rhs) { 647 | return lhs == fixed(rhs); 648 | } 649 | 650 | template ::value>::type> 651 | constexpr bool operator!=(fixed lhs, Number rhs) { 652 | return lhs != fixed(rhs); 653 | } 654 | 655 | template ::value>::type> 656 | constexpr bool operator>(Number lhs, fixed rhs) { 657 | return fixed(lhs) > rhs; 658 | } 659 | 660 | template ::value>::type> 661 | constexpr bool operator<(Number lhs, fixed rhs) { 662 | return fixed(lhs) < rhs; 663 | } 664 | 665 | template ::value>::type> 666 | constexpr bool operator>=(Number lhs, fixed rhs) { 667 | return fixed(lhs) >= rhs; 668 | } 669 | 670 | template ::value>::type> 671 | constexpr bool operator<=(Number lhs, fixed rhs) { 672 | return fixed(lhs) <= rhs; 673 | } 674 | 675 | template ::value>::type> 676 | constexpr bool operator==(Number lhs, fixed rhs) { 677 | return fixed(lhs) == rhs; 678 | } 679 | 680 | template ::value>::type> 681 | constexpr bool operator!=(Number lhs, fixed rhs) { 682 | return fixed(lhs) != rhs; 683 | } 684 | 685 | } 686 | 687 | #undef CONSTEXPR14 688 | 689 | #endif 690 | -------------------------------------------------------------------------------- /fixed/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | add_executable(cpp-utilities-fixed-test 4 | fixed.cpp 5 | ) 6 | target_link_libraries(cpp-utilities-fixed-test 7 | PRIVATE 8 | cpp-utilities::defaults 9 | cpp-utilities::fixed 10 | ) 11 | 12 | add_test( 13 | NAME cpp-utilities-fixed-test 14 | COMMAND $ 15 | ) 16 | 17 | -------------------------------------------------------------------------------- /fixed/test/fixed.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using fixed = numeric::fixed<16, 16>; 7 | 8 | #if __cplusplus >= 201402L 9 | #define STATIC_ASSERT14(expr) static_assert((expr), "") 10 | #else 11 | #define STATIC_ASSERT14(expr) assert((expr)) 12 | #endif 13 | 14 | int main() { 15 | fixed f = 10.5f; 16 | std::cout << f << std::endl; 17 | 18 | // TODO(eteran): perform these tests on the bit-patterns, as float conversions aren't ideal to use 19 | constexpr numeric::fixed<8, 8> a8 = 50.25; 20 | STATIC_ASSERT14(fixed(a8) == fixed(50.25)); 21 | 22 | // TODO(eteran): perform these tests on the bit-patterns, as float comparisons aren't ideal to use 23 | STATIC_ASSERT14((fixed(10.5) * 3) == 31.5); 24 | STATIC_ASSERT14((fixed(10.5) * fixed(3)) == 31.5); 25 | STATIC_ASSERT14((3 * fixed(10.5)) == 31.5); 26 | STATIC_ASSERT14((fixed(10.5) * 3.0) == 31.5); 27 | STATIC_ASSERT14((fixed(10.5) * fixed(3.0)) == 31.5); 28 | STATIC_ASSERT14((3.0 * fixed(10.5)) == 31.5); 29 | 30 | STATIC_ASSERT14(fixed(50) / fixed(5) == fixed(10)); 31 | 32 | STATIC_ASSERT14(-fixed(10) == -10); 33 | STATIC_ASSERT14(+fixed(10) == +10); 34 | 35 | STATIC_ASSERT14(-fixed(10) - 5 == -15); 36 | 37 | STATIC_ASSERT14(++fixed(5) == fixed(6)); 38 | STATIC_ASSERT14(fixed(5)++ == fixed(5)); 39 | STATIC_ASSERT14(--fixed(5) == fixed(4)); 40 | STATIC_ASSERT14(fixed(5)-- == fixed(5)); 41 | 42 | // test some constexpr comparisons stuff 43 | static_assert(fixed{1} > fixed{0}, ""); 44 | static_assert(fixed{0.5} < fixed{1}, ""); 45 | static_assert(fixed{1} == fixed{1}, ""); 46 | static_assert(fixed{0} != fixed{1}, ""); 47 | static_assert(fixed{1} >= fixed{0}, ""); 48 | static_assert(fixed{0.5} <= fixed{1}, ""); 49 | 50 | static_assert(fixed{1} > 0, ""); 51 | static_assert(fixed{0.5} < 1, ""); 52 | static_assert(fixed{1} == 1, ""); 53 | static_assert(fixed{0} != 1, ""); 54 | static_assert(fixed{1} >= 0, ""); 55 | static_assert(fixed{0.5} <= 1, ""); 56 | 57 | static_assert(1 > fixed{0}, ""); 58 | static_assert(0.5 < fixed{1}, ""); 59 | static_assert(1 == fixed{1}, ""); 60 | static_assert(0 != fixed{1}, ""); 61 | static_assert(1 >= fixed{0}, ""); 62 | static_assert(0.5 <= fixed{1}, ""); 63 | 64 | // conversion test 65 | assert(fixed(0x8000).to_uint() == 0x8000); 66 | 67 | return EXIT_SUCCESS; 68 | } 69 | -------------------------------------------------------------------------------- /hash/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | add_library(cpp-utilities-hash INTERFACE) 4 | add_library(cpp-utilities::hash ALIAS cpp-utilities-hash) 5 | 6 | target_include_directories(cpp-utilities-hash 7 | INTERFACE 8 | ${CMAKE_CURRENT_LIST_DIR}/include 9 | ) 10 | 11 | target_link_libraries(cpp-utilities-hash 12 | INTERFACE 13 | cpp-utilities::bitwise 14 | ) 15 | 16 | add_subdirectory(test) 17 | -------------------------------------------------------------------------------- /hash/include/cpp-utilities/crc32.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Evan Teran 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef CRC32_20070328_H_ 26 | #define CRC32_20070328_H_ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | namespace hash { 34 | 35 | namespace detail { 36 | /* this table generated for poly = $edb88320 */ 37 | static const uint32_t crc_table[256] = { 38 | 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 39 | 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 40 | 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 41 | 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 42 | 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 43 | 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 44 | 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 45 | 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 46 | 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 47 | 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 48 | 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 49 | 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 50 | 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 51 | 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 52 | 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 53 | 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 54 | 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 55 | 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 56 | 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 57 | 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 58 | 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 59 | 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 60 | 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 61 | 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 62 | 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 63 | 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 64 | 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 65 | 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 66 | 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 67 | 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 68 | 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 69 | 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 70 | 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 71 | 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 72 | 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 73 | 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 74 | 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 75 | 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 76 | 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 77 | 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 78 | 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 79 | 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 80 | 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 81 | 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 82 | 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 83 | 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 84 | 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 85 | 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 86 | 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 87 | 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 88 | 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 89 | 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 90 | 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 91 | 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 92 | 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 93 | 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 94 | 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 95 | 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 96 | 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 97 | 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 98 | 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 99 | 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 100 | 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 101 | 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d}; 102 | } 103 | 104 | class crc32 { 105 | public: 106 | class digest { 107 | friend class crc32; 108 | 109 | public: 110 | std::string to_string() const { 111 | static const char hexchars[] = "0123456789abcdef"; 112 | 113 | std::string str; 114 | str.reserve(8); 115 | 116 | auto p = std::back_inserter(str); 117 | 118 | *p++ = hexchars[(h_ & 0xf0000000) >> 0x1c]; 119 | *p++ = hexchars[(h_ & 0x0f000000) >> 0x18]; 120 | *p++ = hexchars[(h_ & 0x00f00000) >> 0x14]; 121 | *p++ = hexchars[(h_ & 0x000f0000) >> 0x10]; 122 | *p++ = hexchars[(h_ & 0x0000f000) >> 0x0c]; 123 | *p++ = hexchars[(h_ & 0x00000f00) >> 0x08]; 124 | *p++ = hexchars[(h_ & 0x000000f0) >> 0x04]; 125 | *p++ = hexchars[(h_ & 0x0000000f) >> 0x00]; 126 | 127 | return str; 128 | } 129 | 130 | std::array bytes() const { 131 | std::array b; 132 | b[0] = (h_ & 0x000000ff); 133 | b[1] = (h_ & 0x0000ff00) >> 8; 134 | b[2] = (h_ & 0x00ff0000) >> 16; 135 | b[3] = (h_ & 0xff000000) >> 24; 136 | return b; 137 | } 138 | 139 | bool operator==(const digest &rhs) const { 140 | return h_ == rhs.h_; 141 | } 142 | 143 | bool operator!=(const digest &rhs) const { 144 | return !(*this == rhs); 145 | } 146 | 147 | private: 148 | uint32_t h_ = ~0; 149 | }; 150 | 151 | public: 152 | template 153 | crc32(In first, In last) { 154 | update(first, last); 155 | } 156 | 157 | crc32(const std::string &s) { 158 | update(s); 159 | } 160 | 161 | crc32() = default; 162 | crc32(const crc32 &other) = default; 163 | crc32 &operator=(const crc32 &rhs) = default; 164 | 165 | public: 166 | template 167 | crc32 &update(In first, In last) { 168 | while (first != last) { 169 | update(*first++); 170 | } 171 | return *this; 172 | } 173 | 174 | crc32 &update(uint8_t byte) { 175 | digest_.h_ = (digest_.h_ >> 8) ^ detail::crc_table[(digest_.h_ & 0x000000ff) ^ byte]; 176 | return *this; 177 | } 178 | 179 | crc32 &update(const std::string &s) { 180 | update(s.begin(), s.end()); 181 | return *this; 182 | } 183 | 184 | public: 185 | void swap(crc32 &other) { 186 | using std::swap; 187 | swap(digest_, other.digest_); 188 | } 189 | 190 | void clear() { 191 | digest_ = digest(); 192 | } 193 | 194 | digest finalize() const { 195 | digest d = digest_; 196 | /* invert all bits, and we're done */ 197 | d.h_ = ~d.h_; 198 | return d; 199 | } 200 | 201 | private: 202 | digest digest_; 203 | }; 204 | 205 | } 206 | 207 | #endif 208 | -------------------------------------------------------------------------------- /hash/include/cpp-utilities/md5.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Evan Teran 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef MD5_20070328_H_ 26 | #define MD5_20070328_H_ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | namespace hash { 38 | 39 | namespace detail { 40 | 41 | constexpr uint32_t F(uint32_t x, uint32_t y, uint32_t z) { 42 | return (x & y) | (~x & z); 43 | } 44 | 45 | constexpr uint32_t G(uint32_t x, uint32_t y, uint32_t z) { 46 | return (z & x) | (~z & y); 47 | } 48 | 49 | constexpr uint32_t H(uint32_t x, uint32_t y, uint32_t z) { 50 | return x ^ y ^ z; 51 | } 52 | 53 | constexpr uint32_t I(uint32_t x, uint32_t y, uint32_t z) { 54 | return y ^ (x | ~z); 55 | } 56 | 57 | template 58 | uint32_t xfrm(Func f, uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t s, uint32_t ac) { 59 | a += f(b, c, d) + x + ac; 60 | a = bitwise::rotate_left(a, s) + b; 61 | return a; 62 | } 63 | 64 | } 65 | 66 | class md5 { 67 | public: 68 | struct state { 69 | static constexpr int BlockSize = 64; 70 | 71 | uint8_t block_[BlockSize]; // input buffer 72 | uint64_t length_ = 0; // number of bits 73 | size_t index_ = 0; 74 | }; 75 | 76 | class digest { 77 | friend class md5; 78 | 79 | public: 80 | std::string to_string() const { 81 | static const char hexchars[] = "0123456789abcdef"; 82 | 83 | std::string str; 84 | str.reserve(32); 85 | 86 | auto p = std::back_inserter(str); 87 | 88 | for (int i = 0; i < 4; ++i) { 89 | *p++ = hexchars[(h_[i] & 0x000000f0) >> 0x04]; 90 | *p++ = hexchars[(h_[i] & 0x0000000f) >> 0x00]; 91 | *p++ = hexchars[(h_[i] & 0x0000f000) >> 0x0c]; 92 | *p++ = hexchars[(h_[i] & 0x00000f00) >> 0x08]; 93 | *p++ = hexchars[(h_[i] & 0x00f00000) >> 0x14]; 94 | *p++ = hexchars[(h_[i] & 0x000f0000) >> 0x10]; 95 | *p++ = hexchars[(h_[i] & 0xf0000000) >> 0x1c]; 96 | *p++ = hexchars[(h_[i] & 0x0f000000) >> 0x18]; 97 | } 98 | 99 | return str; 100 | } 101 | 102 | std::array bytes() const { 103 | std::array b; 104 | 105 | for (int i = 0; i < 4; ++i) { 106 | b[0 + (i * 4)] = (h_[i] & 0x000000ff); 107 | b[1 + (i * 4)] = (h_[i] & 0x0000ff00) >> 8; 108 | b[2 + (i * 4)] = (h_[i] & 0x00ff0000) >> 16; 109 | b[3 + (i * 4)] = (h_[i] & 0xff000000) >> 24; 110 | } 111 | 112 | return b; 113 | } 114 | 115 | bool operator==(const digest &rhs) const { 116 | return std::memcmp(h_, rhs.h_, sizeof(h_)) == 0; 117 | } 118 | 119 | bool operator!=(const digest &rhs) const { 120 | return !(*this == rhs); 121 | } 122 | 123 | private: 124 | uint32_t h_[4] = { 125 | 0x67452301, 126 | 0xefcdab89, 127 | 0x98badcfe, 128 | 0x10325476, 129 | }; 130 | }; 131 | 132 | public: 133 | template 134 | md5(In first, In last) { 135 | update(first, last); 136 | } 137 | 138 | md5(const std::string &s) { 139 | update(s); 140 | } 141 | 142 | md5() = default; 143 | md5(const md5 &other) = default; 144 | md5 &operator=(const md5 &rhs) = default; 145 | 146 | public: 147 | template 148 | md5 &update(In first, In last) { 149 | while (first != last) { 150 | update(*first++); 151 | } 152 | return *this; 153 | } 154 | 155 | md5 &update(uint8_t byte) { 156 | state_.block_[state_.index_++] = byte; 157 | 158 | state_.length_ += 8; 159 | 160 | if (state_.index_ == state::BlockSize) { 161 | process_block(&state_, &digest_); 162 | } 163 | return *this; 164 | } 165 | 166 | md5 &update(const std::string &s) { 167 | update(s.begin(), s.end()); 168 | return *this; 169 | } 170 | 171 | public: 172 | void swap(md5 &other) { 173 | using std::swap; 174 | 175 | swap(digest_, other.digest_); 176 | swap(state_, other.state_); 177 | } 178 | 179 | void clear() { 180 | digest_ = digest(); 181 | state_ = state(); 182 | } 183 | 184 | digest finalize() const { 185 | // make copies so this isn't a mutating operation 186 | state s = state_; 187 | digest d = digest_; 188 | 189 | s.block_[s.index_] = 0x80; 190 | 191 | const size_t n = s.index_++; 192 | 193 | if (n > 55) { 194 | while (s.index_ < state::BlockSize) { 195 | s.block_[s.index_++] = 0; 196 | } 197 | 198 | process_block(&s, &d); 199 | } 200 | 201 | while (s.index_ < 56) { 202 | s.block_[s.index_++] = 0; 203 | } 204 | 205 | // append length (before padding) 206 | s.block_[56] = (s.length_) & 0xff; 207 | s.block_[57] = (s.length_ >> 8) & 0xff; 208 | s.block_[58] = (s.length_ >> 16) & 0xff; 209 | s.block_[59] = (s.length_ >> 24) & 0xff; 210 | s.block_[60] = (s.length_ >> 32) & 0xff; 211 | s.block_[61] = (s.length_ >> 40) & 0xff; 212 | s.block_[62] = (s.length_ >> 48) & 0xff; 213 | s.block_[63] = (s.length_ >> 56) & 0xff; 214 | process_block(&s, &d); 215 | 216 | return d; 217 | } 218 | 219 | private: 220 | static void process_block(state *state, digest *digest) { 221 | auto FF = [](uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t s, uint32_t ac) { 222 | return detail::xfrm(detail::F, a, b, c, d, x, s, ac); 223 | }; 224 | 225 | auto GG = [](uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t s, uint32_t ac) { 226 | return detail::xfrm(detail::G, a, b, c, d, x, s, ac); 227 | }; 228 | 229 | auto HH = [](uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t s, uint32_t ac) { 230 | return detail::xfrm(detail::H, a, b, c, d, x, s, ac); 231 | }; 232 | 233 | auto II = [](uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t s, uint32_t ac) { 234 | return detail::xfrm(detail::I, a, b, c, d, x, s, ac); 235 | }; 236 | 237 | static constexpr int S11 = 7; 238 | static constexpr int S12 = 12; 239 | static constexpr int S13 = 17; 240 | static constexpr int S14 = 22; 241 | static constexpr int S21 = 5; 242 | static constexpr int S22 = 9; 243 | static constexpr int S23 = 14; 244 | static constexpr int S24 = 20; 245 | static constexpr int S31 = 4; 246 | static constexpr int S32 = 11; 247 | static constexpr int S33 = 16; 248 | static constexpr int S34 = 23; 249 | static constexpr int S41 = 6; 250 | static constexpr int S42 = 10; 251 | static constexpr int S43 = 15; 252 | static constexpr int S44 = 21; 253 | 254 | uint32_t a = digest->h_[0]; 255 | uint32_t b = digest->h_[1]; 256 | uint32_t c = digest->h_[2]; 257 | uint32_t d = digest->h_[3]; 258 | 259 | uint32_t x[16]; 260 | std::memcpy(x, state->block_, sizeof(x)); 261 | 262 | /* Round 1 */ 263 | a = FF(a, b, c, d, x[0], S11, 0xd76aa478); /* 1 */ 264 | d = FF(d, a, b, c, x[1], S12, 0xe8c7b756); /* 2 */ 265 | c = FF(c, d, a, b, x[2], S13, 0x242070db); /* 3 */ 266 | b = FF(b, c, d, a, x[3], S14, 0xc1bdceee); /* 4 */ 267 | a = FF(a, b, c, d, x[4], S11, 0xf57c0faf); /* 5 */ 268 | d = FF(d, a, b, c, x[5], S12, 0x4787c62a); /* 6 */ 269 | c = FF(c, d, a, b, x[6], S13, 0xa8304613); /* 7 */ 270 | b = FF(b, c, d, a, x[7], S14, 0xfd469501); /* 8 */ 271 | a = FF(a, b, c, d, x[8], S11, 0x698098d8); /* 9 */ 272 | d = FF(d, a, b, c, x[9], S12, 0x8b44f7af); /* 10 */ 273 | c = FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ 274 | b = FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ 275 | a = FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ 276 | d = FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ 277 | c = FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ 278 | b = FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ 279 | 280 | /* Round 2 */ 281 | a = GG(a, b, c, d, x[1], S21, 0xf61e2562); /* 17 */ 282 | d = GG(d, a, b, c, x[6], S22, 0xc040b340); /* 18 */ 283 | c = GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ 284 | b = GG(b, c, d, a, x[0], S24, 0xe9b6c7aa); /* 20 */ 285 | a = GG(a, b, c, d, x[5], S21, 0xd62f105d); /* 21 */ 286 | d = GG(d, a, b, c, x[10], S22, 0x02441453); /* 22 */ 287 | c = GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ 288 | b = GG(b, c, d, a, x[4], S24, 0xe7d3fbc8); /* 24 */ 289 | a = GG(a, b, c, d, x[9], S21, 0x21e1cde6); /* 25 */ 290 | d = GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ 291 | c = GG(c, d, a, b, x[3], S23, 0xf4d50d87); /* 27 */ 292 | b = GG(b, c, d, a, x[8], S24, 0x455a14ed); /* 28 */ 293 | a = GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ 294 | d = GG(d, a, b, c, x[2], S22, 0xfcefa3f8); /* 30 */ 295 | c = GG(c, d, a, b, x[7], S23, 0x676f02d9); /* 31 */ 296 | b = GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ 297 | 298 | /* Round 3 */ 299 | a = HH(a, b, c, d, x[5], S31, 0xfffa3942); /* 33 */ 300 | d = HH(d, a, b, c, x[8], S32, 0x8771f681); /* 34 */ 301 | c = HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ 302 | b = HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ 303 | a = HH(a, b, c, d, x[1], S31, 0xa4beea44); /* 37 */ 304 | d = HH(d, a, b, c, x[4], S32, 0x4bdecfa9); /* 38 */ 305 | c = HH(c, d, a, b, x[7], S33, 0xf6bb4b60); /* 39 */ 306 | b = HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ 307 | a = HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ 308 | d = HH(d, a, b, c, x[0], S32, 0xeaa127fa); /* 42 */ 309 | c = HH(c, d, a, b, x[3], S33, 0xd4ef3085); /* 43 */ 310 | b = HH(b, c, d, a, x[6], S34, 0x04881d05); /* 44 */ 311 | a = HH(a, b, c, d, x[9], S31, 0xd9d4d039); /* 45 */ 312 | d = HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ 313 | c = HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ 314 | b = HH(b, c, d, a, x[2], S34, 0xc4ac5665); /* 48 */ 315 | 316 | /* Round 4 */ 317 | a = II(a, b, c, d, x[0], S41, 0xf4292244); /* 49 */ 318 | d = II(d, a, b, c, x[7], S42, 0x432aff97); /* 50 */ 319 | c = II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ 320 | b = II(b, c, d, a, x[5], S44, 0xfc93a039); /* 52 */ 321 | a = II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ 322 | d = II(d, a, b, c, x[3], S42, 0x8f0ccc92); /* 54 */ 323 | c = II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ 324 | b = II(b, c, d, a, x[1], S44, 0x85845dd1); /* 56 */ 325 | a = II(a, b, c, d, x[8], S41, 0x6fa87e4f); /* 57 */ 326 | d = II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ 327 | c = II(c, d, a, b, x[6], S43, 0xa3014314); /* 59 */ 328 | b = II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ 329 | a = II(a, b, c, d, x[4], S41, 0xf7537e82); /* 61 */ 330 | d = II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ 331 | c = II(c, d, a, b, x[2], S43, 0x2ad7d2bb); /* 63 */ 332 | b = II(b, c, d, a, x[9], S44, 0xeb86d391); /* 64 */ 333 | 334 | digest->h_[0] += a; 335 | digest->h_[1] += b; 336 | digest->h_[2] += c; 337 | digest->h_[3] += d; 338 | 339 | state->index_ = 0; 340 | } 341 | 342 | private: 343 | state state_; 344 | digest digest_; 345 | }; 346 | 347 | } 348 | 349 | #endif 350 | -------------------------------------------------------------------------------- /hash/include/cpp-utilities/sha1.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Evan Teran 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef SHA1_20151007_H_ 26 | #define SHA1_20151007_H_ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | namespace hash { 38 | 39 | class sha1 { 40 | public: 41 | struct state { 42 | static constexpr int BlockSize = 64; 43 | 44 | uint8_t block_[BlockSize]; // 512-bit message blocks 45 | uint64_t length_ = 0; // message length in bits 46 | std::size_t index_ = 0; // index into message block array 47 | }; 48 | 49 | class digest { 50 | friend class sha1; 51 | 52 | public: 53 | std::string to_string() const { 54 | static const char hexchars[] = "0123456789abcdef"; 55 | std::string str; 56 | str.reserve(40); 57 | 58 | auto p = std::back_inserter(str); 59 | 60 | for (int i = 0; i < 5; ++i) { 61 | *p++ = hexchars[(h_[i] & 0xf0000000) >> 0x1c]; 62 | *p++ = hexchars[(h_[i] & 0x0f000000) >> 0x18]; 63 | *p++ = hexchars[(h_[i] & 0x00f00000) >> 0x14]; 64 | *p++ = hexchars[(h_[i] & 0x000f0000) >> 0x10]; 65 | *p++ = hexchars[(h_[i] & 0x0000f000) >> 0x0c]; 66 | *p++ = hexchars[(h_[i] & 0x00000f00) >> 0x08]; 67 | *p++ = hexchars[(h_[i] & 0x000000f0) >> 0x04]; 68 | *p++ = hexchars[(h_[i] & 0x0000000f) >> 0x00]; 69 | } 70 | 71 | return str; 72 | } 73 | 74 | std::array bytes() const { 75 | std::array b; 76 | 77 | for (int i = 0; i < 5; ++i) { 78 | b[3 + (i * 4)] = (h_[i] & 0x000000ff); 79 | b[2 + (i * 4)] = (h_[i] & 0x0000ff00) >> 8; 80 | b[1 + (i * 4)] = (h_[i] & 0x00ff0000) >> 16; 81 | b[0 + (i * 4)] = (h_[i] & 0xff000000) >> 24; 82 | } 83 | 84 | return b; 85 | } 86 | bool operator==(const digest &rhs) const { 87 | return std::memcmp(h_, rhs.h_, sizeof(h_)) == 0; 88 | } 89 | 90 | bool operator!=(const digest &rhs) const { 91 | return !(*this == rhs); 92 | } 93 | 94 | private: 95 | uint32_t h_[5] = { 96 | 0x67452301, 97 | 0xefcdab89, 98 | 0x98badcfe, 99 | 0x10325476, 100 | 0xc3d2e1f0, 101 | }; 102 | }; 103 | 104 | public: 105 | template 106 | sha1(In first, In last) { 107 | update(first, last); 108 | } 109 | 110 | sha1(const std::string &s) { 111 | update(s); 112 | } 113 | 114 | sha1() = default; 115 | sha1(const sha1 &other) = default; 116 | sha1 &operator=(const sha1 &rhs) = default; 117 | 118 | public: 119 | template 120 | sha1 &update(In first, In last) { 121 | while (first != last) { 122 | update(*first++); 123 | } 124 | return *this; 125 | } 126 | 127 | sha1 &update(uint8_t byte) { 128 | state_.block_[state_.index_++] = byte; 129 | 130 | state_.length_ += 8; 131 | 132 | if (state_.index_ == state::BlockSize) { 133 | process_block(&state_, &digest_); 134 | } 135 | 136 | return *this; 137 | } 138 | 139 | sha1 &update(const std::string &s) { 140 | update(s.begin(), s.end()); 141 | return *this; 142 | } 143 | 144 | public: 145 | void swap(sha1 &other) { 146 | using std::swap; 147 | 148 | swap(digest_, other.digest_); 149 | swap(state_, other.state_); 150 | } 151 | 152 | void clear() { 153 | digest_ = digest(); 154 | state_ = state(); 155 | } 156 | 157 | digest finalize() const { 158 | // make copies so this isn't a mutating operation 159 | state s = state_; 160 | digest d = digest_; 161 | 162 | s.block_[s.index_] = 0x80; 163 | 164 | const size_t n = s.index_++; 165 | 166 | if (n > 55) { 167 | while (s.index_ < state::BlockSize) { 168 | s.block_[s.index_++] = 0; 169 | } 170 | 171 | process_block(&s, &d); 172 | } 173 | 174 | while (s.index_ < 56) { 175 | s.block_[s.index_++] = 0; 176 | } 177 | 178 | // Store the message length as the last 8 octets 179 | s.block_[56] = (s.length_ >> 56) & 0xff; 180 | s.block_[57] = (s.length_ >> 48) & 0xff; 181 | s.block_[58] = (s.length_ >> 40) & 0xff; 182 | s.block_[59] = (s.length_ >> 32) & 0xff; 183 | s.block_[60] = (s.length_ >> 24) & 0xff; 184 | s.block_[61] = (s.length_ >> 16) & 0xff; 185 | s.block_[62] = (s.length_ >> 8) & 0xff; 186 | s.block_[63] = (s.length_) & 0xff; 187 | process_block(&s, &d); 188 | 189 | return d; 190 | } 191 | 192 | private: 193 | static void process_block(state *state, digest *digest) { 194 | static constexpr uint32_t K[] = { 195 | 0x5a827999, 196 | 0x6ed9eba1, 197 | 0x8f1bbcdc, 198 | 0xca62c1d6}; 199 | 200 | uint32_t W[80]; // Word sequence 201 | 202 | // Initialize the first 16 words in the array W 203 | for (int t = 0; t < 16; ++t) { 204 | W[t] = static_cast(state->block_[t * 4 + 0]) << 24; 205 | W[t] |= static_cast(state->block_[t * 4 + 1]) << 16; 206 | W[t] |= static_cast(state->block_[t * 4 + 2]) << 8; 207 | W[t] |= static_cast(state->block_[t * 4 + 3]); 208 | } 209 | 210 | #if 0 211 | for(int t = 16; t < 80; ++t) { 212 | W[t] = bitwise::rotate_left(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16], 1); 213 | } 214 | #else 215 | // slight improvement if we attempt to do vectorization: 216 | // http://software.intel.com/en-us/articles/improving-the-performance-of-the-secure-hash-algorithm-1/ 217 | for (int t = 16; t < 32; ++t) { 218 | W[t] = bitwise::rotate_left(W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16], 1); 219 | } 220 | 221 | for (int t = 32; t < 80; ++t) { 222 | W[t] = bitwise::rotate_left(W[t - 6] ^ W[t - 16] ^ W[t - 28] ^ W[t - 32], 2); 223 | } 224 | #endif 225 | 226 | uint32_t A = digest->h_[0]; 227 | uint32_t B = digest->h_[1]; 228 | uint32_t C = digest->h_[2]; 229 | uint32_t D = digest->h_[3]; 230 | uint32_t E = digest->h_[4]; 231 | 232 | for (int t = 0; t < 20; ++t) { 233 | const uint32_t temp = bitwise::rotate_left(A, 5) + ((B & C) | ((~B) & D)) + E + W[t] + K[0]; 234 | 235 | E = D; 236 | D = C; 237 | C = bitwise::rotate_left(B, 30); 238 | B = A; 239 | A = temp; 240 | } 241 | 242 | for (int t = 20; t < 40; ++t) { 243 | const uint32_t temp = bitwise::rotate_left(A, 5) + (B ^ C ^ D) + E + W[t] + K[1]; 244 | 245 | E = D; 246 | D = C; 247 | C = bitwise::rotate_left(B, 30); 248 | B = A; 249 | A = temp; 250 | } 251 | 252 | for (int t = 40; t < 60; ++t) { 253 | const uint32_t temp = bitwise::rotate_left(A, 5) + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2]; 254 | 255 | E = D; 256 | D = C; 257 | C = bitwise::rotate_left(B, 30); 258 | B = A; 259 | A = temp; 260 | } 261 | 262 | for (int t = 60; t < 80; ++t) { 263 | const uint32_t temp = bitwise::rotate_left(A, 5) + (B ^ C ^ D) + E + W[t] + K[3]; 264 | 265 | E = D; 266 | D = C; 267 | C = bitwise::rotate_left(B, 30); 268 | B = A; 269 | A = temp; 270 | } 271 | 272 | digest->h_[0] += A; 273 | digest->h_[1] += B; 274 | digest->h_[2] += C; 275 | digest->h_[3] += D; 276 | digest->h_[4] += E; 277 | 278 | state->index_ = 0; 279 | } 280 | 281 | private: 282 | state state_; 283 | digest digest_; 284 | }; 285 | 286 | } 287 | 288 | #endif 289 | -------------------------------------------------------------------------------- /hash/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | add_executable(cpp-utilities-hash-test 4 | test.cpp 5 | ) 6 | 7 | target_link_libraries(cpp-utilities-hash-test 8 | PRIVATE 9 | cpp-utilities::defaults 10 | cpp-utilities::hash 11 | ) 12 | 13 | add_test( 14 | NAME cpp-utilities-hash-test 15 | COMMAND $ 16 | ) 17 | 18 | -------------------------------------------------------------------------------- /hash/test/test.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "cpp-utilities/crc32.h" 3 | #include "cpp-utilities/md5.h" 4 | #include "cpp-utilities/sha1.h" 5 | #include 6 | #include 7 | 8 | int main() { 9 | 10 | { 11 | // --------------------- Test sha1 --------------------- 12 | const std::string s = "Hello World"; 13 | hash::sha1 sha1(s.begin(), s.end()); 14 | 15 | auto d1 = sha1.finalize(); 16 | std::cout << d1.to_string() << std::endl; 17 | assert(d1.to_string() == "0a4d55a8d778e5022fab701977c5d840bbc486d0"); 18 | 19 | sha1.update('!'); 20 | 21 | auto d2 = sha1.finalize(); 22 | std::cout << d2.to_string() << std::endl; 23 | assert(d2.to_string() == "2ef7bde608ce5404e97d5f042f95f89f1c232871"); 24 | 25 | auto d3 = hash::sha1().finalize(); 26 | std::cout << d3.to_string() << std::endl; 27 | assert(d3.to_string() == "da39a3ee5e6b4b0d3255bfef95601890afd80709"); 28 | 29 | auto d4 = hash::sha1("The quick brown fox jumps over the lazy dog.").finalize(); 30 | std::cout << d4.to_string() << std::endl; 31 | assert(d4.to_string() == "408d94384216f890ff7a0c3528e8bed1e0b01621"); 32 | 33 | auto d5 = hash::sha1("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA").finalize(); 34 | std::cout << d5.to_string() << std::endl; 35 | assert(d5.to_string() == "5021b3d42aa093bffc34eedd7a1455f3624bc552"); 36 | 37 | auto d6 = hash::sha1("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA").finalize(); 38 | std::cout << d6.to_string() << std::endl; 39 | assert(d6.to_string() == "6b45e3cf1eb3324b9fd4df3b83d89c4c2c4ca896"); 40 | 41 | auto d7 = hash::sha1("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA").finalize(); 42 | std::cout << d7.to_string() << std::endl; 43 | assert(d7.to_string() == "e8d6ea5c627fc8676fa662677b028640844dc35c"); 44 | 45 | auto d8 = hash::sha1({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}).finalize(); 46 | std::cout << d8.to_string() << std::endl; 47 | assert(d8.to_string() == "c5391e308af25b42d5934d6a201a34e898d255c6"); 48 | } 49 | 50 | // --------------------- Test MD5 --------------------- 51 | { 52 | const std::string s = "Hello World"; 53 | hash::md5 md5(s.begin(), s.end()); 54 | 55 | auto d1 = md5.finalize(); 56 | std::cout << d1.to_string() << std::endl; 57 | assert(d1.to_string() == "b10a8db164e0754105b7a99be72e3fe5"); 58 | 59 | md5.update('!'); 60 | 61 | auto d2 = md5.finalize(); 62 | std::cout << d2.to_string() << std::endl; 63 | assert(d2.to_string() == "ed076287532e86365e841e92bfc50d8c"); 64 | 65 | auto d3 = hash::md5().finalize(); 66 | std::cout << d3.to_string() << std::endl; 67 | assert(d3.to_string() == "d41d8cd98f00b204e9800998ecf8427e"); 68 | 69 | auto d4 = hash::md5("The quick brown fox jumps over the lazy dog.").finalize(); 70 | std::cout << d4.to_string() << std::endl; 71 | assert(d4.to_string() == "e4d909c290d0fb1ca068ffaddf22cbd0"); 72 | 73 | auto d5 = hash::md5("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA").finalize(); 74 | std::cout << d5.to_string() << std::endl; 75 | assert(d5.to_string() == "e38a93ffe074a99b3fed47dfbe37db21"); 76 | 77 | auto d6 = hash::md5("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA").finalize(); 78 | std::cout << d6.to_string() << std::endl; 79 | assert(d6.to_string() == "a2f3e2024931bd470555002aa5ccc010"); 80 | 81 | auto d7 = hash::md5("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA").finalize(); 82 | std::cout << d7.to_string() << std::endl; 83 | assert(d7.to_string() == "9a7c38569e5a96e3cfbad45fb9ce5209"); 84 | 85 | auto d8 = hash::md5({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}).finalize(); 86 | std::cout << d8.to_string() << std::endl; 87 | assert(d8.to_string() == "70903e79b7575e3f4e7ffa15c2608ac7"); 88 | } 89 | 90 | // --------------------- Test CRC32 --------------------- 91 | { 92 | const std::string s = "Hello World"; 93 | hash::crc32 crc32(s.begin(), s.end()); 94 | 95 | auto d1 = crc32.finalize(); 96 | std::cout << d1.to_string() << std::endl; 97 | assert(d1.to_string() == "4a17b156"); 98 | 99 | crc32.update('!'); 100 | 101 | auto d2 = crc32.finalize(); 102 | std::cout << d2.to_string() << std::endl; 103 | assert(d2.to_string() == "1c291ca3"); 104 | 105 | auto d3 = hash::crc32().finalize(); 106 | std::cout << d3.to_string() << std::endl; 107 | assert(d3.to_string() == "00000000"); 108 | 109 | auto d4 = hash::crc32("The quick brown fox jumps over the lazy dog.").finalize(); 110 | std::cout << d4.to_string() << std::endl; 111 | assert(d4.to_string() == "519025e9"); 112 | 113 | auto d5 = hash::crc32("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA").finalize(); 114 | std::cout << d5.to_string() << std::endl; 115 | assert(d5.to_string() == "be5161f4"); 116 | 117 | auto d6 = hash::crc32("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA").finalize(); 118 | std::cout << d6.to_string() << std::endl; 119 | assert(d6.to_string() == "69b7f9ef"); 120 | 121 | auto d7 = hash::crc32("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA").finalize(); 122 | std::cout << d7.to_string() << std::endl; 123 | assert(d7.to_string() == "e305d69b"); 124 | 125 | auto d8 = hash::crc32({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}).finalize(); 126 | std::cout << d8.to_string() << std::endl; 127 | assert(d8.to_string() == "2520577b"); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /logger/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | add_library(cpp-utilities-logger INTERFACE) 4 | add_library(cpp-utilities::logger ALIAS cpp-utilities-logger) 5 | 6 | target_include_directories(cpp-utilities-logger 7 | INTERFACE 8 | ${CMAKE_CURRENT_LIST_DIR}/include 9 | ) 10 | 11 | add_subdirectory(test) 12 | -------------------------------------------------------------------------------- /logger/include/cpp-utilities/logger.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2021 Evan Teran 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef LOGGER_HPP_ 26 | #define LOGGER_HPP_ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #if defined __has_include 38 | #if __has_include() 39 | #include 40 | #elif __has_include() 41 | #include 42 | #endif 43 | #endif 44 | 45 | namespace logger { 46 | 47 | enum Level { 48 | Emerg = 0, // Emergency - A "panic" condition usually affecting multiple apps/servers/sites. At this level it would usually notify all tech staff on call. 49 | Alert = 1, // Alert - Should be corrected immediately, therefore notify staff who can fix the problem. An example would be the loss of a primary ISP connection. 50 | Crit = 2, // Critical - Should be corrected immediately, but indicates failure in a secondary system, an example is a loss of a backup ISP connection. 51 | Error = 3, // Error - Non-urgent failures, these should be relayed to developers or admins; each item must be resolved within a given time. 52 | Warn = 4, // Warning - Warning messages, not an error, but indication that an error will occur if action is not taken, e.g. file system 85% full - each item must be resolved within a given time. 53 | Notice = 5, // Notice - Events that are unusual but not error conditions - might be summarized in an email to developers or admins to spot potential problems - no immediate action required. 54 | Info = 6, // Informational - Normal operational messages - may be harvested for reporting, measuring throughput, etc. - no action required. 55 | Debug = 7 // Debug - Info useful to developers for debugging the application, not useful during operations. 56 | }; 57 | 58 | namespace detail { 59 | 60 | #if defined __has_include 61 | #if __has_include() 62 | using sl = std::source_location; 63 | #elif __has_include() 64 | #include 65 | using sl = std::experimental::source_location; 66 | #endif 67 | #endif 68 | 69 | //------------------------------------------------------------------------------ 70 | // Name: parse_bool_or_default 71 | //------------------------------------------------------------------------------ 72 | inline bool parse_bool_or_default(const char *str, bool default_value) { 73 | if (!str) { 74 | return default_value; 75 | } 76 | 77 | return strcmp(str, "1") == 0 || strcmp(str, "Y") == 0 || strcmp(str, "TRUE") == 0; 78 | } 79 | 80 | //------------------------------------------------------------------------------ 81 | // Name: minimum_level 82 | //------------------------------------------------------------------------------ 83 | inline Level minimum_level() { 84 | static const Level level = [] { 85 | if (const char *const e = getenv("LOG_LEVEL")) { 86 | 87 | if (e[0] != '\0' && e[1] == '\0') { 88 | switch (e[0]) { 89 | case '7': 90 | return Debug; 91 | case '6': 92 | return Info; 93 | case '5': 94 | return Notice; 95 | case '4': 96 | return Warn; 97 | case '3': 98 | return Error; 99 | case '2': 100 | return Crit; 101 | case '1': 102 | return Alert; 103 | case '0': 104 | return Emerg; 105 | } 106 | } 107 | 108 | if (strcmp(e, "DEBUG") == 0) { 109 | return Debug; 110 | } else if (strcmp(e, "INFO") == 0) { 111 | return Info; 112 | } else if (strcmp(e, "NOTICE") == 0) { 113 | return Notice; 114 | } else if (strcmp(e, "WARN") == 0) { 115 | return Warn; 116 | } else if (strcmp(e, "ERROR") == 0) { 117 | return Error; 118 | } else if (strcmp(e, "CRIT") == 0) { 119 | return Crit; 120 | } else if (strcmp(e, "ALERT") == 0) { 121 | return Alert; 122 | } else if (strcmp(e, "EMERG") == 0) { 123 | return Emerg; 124 | } 125 | } 126 | 127 | return Notice; 128 | }(); 129 | 130 | return level; 131 | } 132 | 133 | //------------------------------------------------------------------------------ 134 | // Name: colorize 135 | //------------------------------------------------------------------------------ 136 | inline bool colorize() { 137 | static const bool c = [] { 138 | return parse_bool_or_default(getenv("LOG_COLOR"), true); 139 | }(); 140 | 141 | return c; 142 | } 143 | 144 | //------------------------------------------------------------------------------ 145 | // Name: timedate 146 | //------------------------------------------------------------------------------ 147 | inline bool timedate() { 148 | static const bool t = [] { 149 | return parse_bool_or_default(getenv("LOG_TIME"), true); 150 | }(); 151 | 152 | return t; 153 | } 154 | 155 | //------------------------------------------------------------------------------ 156 | // Name: fullpath 157 | //------------------------------------------------------------------------------ 158 | inline bool fullpath() { 159 | static const bool t = [] { 160 | return parse_bool_or_default(getenv("LOG_FULLPATH"), false); 161 | }(); 162 | 163 | return t; 164 | } 165 | 166 | //------------------------------------------------------------------------------ 167 | // Name: basename 168 | //------------------------------------------------------------------------------ 169 | inline std::string_view basename(std::string_view path) { 170 | 171 | if (fullpath()) { 172 | return path; 173 | } 174 | 175 | std::string_view::size_type n = path.rfind('/'); 176 | if (n == std::string_view::npos) { 177 | return path; 178 | } 179 | 180 | return path.substr(n + 1); 181 | } 182 | 183 | } 184 | 185 | //------------------------------------------------------------------------------ 186 | // Name: message 187 | //------------------------------------------------------------------------------ 188 | inline void message(Level level, std::string_view msg, const detail::sl &location) { 189 | 190 | static std::mutex log_mutex; 191 | 192 | if (level <= detail::minimum_level()) { 193 | 194 | std::stringstream ss; 195 | 196 | if (detail::timedate()) { 197 | auto now = std::chrono::system_clock::now(); 198 | auto in_time_t = std::chrono::system_clock::to_time_t(now); 199 | 200 | ss << std::put_time(std::gmtime(&in_time_t), "%Y-%m-%dT%H:%M:%SZ") << ' '; 201 | } 202 | 203 | ss << '[' << detail::basename(location.file_name()) << ':' << location.line() << "] "; 204 | 205 | if (detail::colorize()) { 206 | 207 | static const char *const ansi_clear = "\x1B[0m"; 208 | static const char *const ansi_emerg = "\x1B[7;91m"; 209 | static const char *const ansi_alert = "\x1B[3;91m"; 210 | static const char *const ansi_crit = "\x1B[1;91m"; 211 | static const char *const ansi_warn = "\x1B[92m"; 212 | static const char *const ansi_error = "\x1B[93m"; 213 | static const char *const ansi_notice = "\x1B[94m"; 214 | static const char *const ansi_info = "\x1B[97m"; 215 | static const char *const ansi_debug = "\x1B[98m"; 216 | 217 | switch (level) { 218 | case Emerg: 219 | ss << '[' << ansi_emerg << "EMERG" << ansi_clear << " ]: "; 220 | break; 221 | case Alert: 222 | ss << '[' << ansi_alert << "ALERT" << ansi_clear << " ]: "; 223 | break; 224 | case Crit: 225 | ss << '[' << ansi_crit << "CRIT" << ansi_clear << " ]: "; 226 | break; 227 | case Error: 228 | ss << '[' << ansi_error << "ERROR" << ansi_clear << " ]: "; 229 | break; 230 | case Warn: 231 | ss << '[' << ansi_warn << "WARN" << ansi_clear << " ]: "; 232 | break; 233 | case Notice: 234 | ss << '[' << ansi_notice << "NOTICE" << ansi_clear << "]: "; 235 | break; 236 | case Info: 237 | ss << '[' << ansi_info << "INFO" << ansi_clear << " ]: "; 238 | break; 239 | case Debug: 240 | ss << '[' << ansi_debug << "DEBUG" << ansi_clear << " ]: "; 241 | break; 242 | } 243 | } else { 244 | switch (level) { 245 | case Emerg: 246 | ss << "[EMERG ]: "; 247 | break; 248 | case Alert: 249 | ss << "[ALERT ]: "; 250 | break; 251 | case Crit: 252 | ss << "[CRIT ]: "; 253 | break; 254 | case Error: 255 | ss << "[ERROR ]: "; 256 | break; 257 | case Warn: 258 | ss << "[WARN ]: "; 259 | break; 260 | case Notice: 261 | ss << "[NOTICE]: "; 262 | break; 263 | case Info: 264 | ss << "[INFO ]: "; 265 | break; 266 | case Debug: 267 | ss << "[DEBUG ]: "; 268 | break; 269 | } 270 | } 271 | 272 | ss << msg; 273 | 274 | // we render the whole message into the buffer above 275 | // so that the lock is held for the shortest amount of 276 | // time possible 277 | std::unique_lock lock(log_mutex); 278 | std::clog << ss.str() << std::endl; 279 | } 280 | } 281 | 282 | //------------------------------------------------------------------------------ 283 | // Name: debug 284 | //------------------------------------------------------------------------------ 285 | inline void debug(std::string_view msg, const detail::sl &location = detail::sl::current()) { 286 | message(Debug, msg, location); 287 | } 288 | 289 | //------------------------------------------------------------------------------ 290 | // Name: info 291 | //------------------------------------------------------------------------------ 292 | inline void info(std::string_view msg, const detail::sl &location = detail::sl::current()) { 293 | message(Info, msg, location); 294 | } 295 | 296 | //------------------------------------------------------------------------------ 297 | // Name: notice 298 | //------------------------------------------------------------------------------ 299 | inline void notice(std::string_view msg, const detail::sl &location = detail::sl::current()) { 300 | message(Notice, msg, location); 301 | } 302 | 303 | //------------------------------------------------------------------------------ 304 | // Name: warning 305 | //------------------------------------------------------------------------------ 306 | inline void warning(std::string_view msg, const detail::sl &location = detail::sl::current()) { 307 | message(Warn, msg, location); 308 | } 309 | 310 | //------------------------------------------------------------------------------ 311 | // Name: error 312 | //------------------------------------------------------------------------------ 313 | inline void error(std::string_view msg, const detail::sl &location = detail::sl::current()) { 314 | message(Error, msg, location); 315 | } 316 | 317 | //------------------------------------------------------------------------------ 318 | // Name: critical 319 | //------------------------------------------------------------------------------ 320 | inline void critical(std::string_view msg, const detail::sl &location = detail::sl::current()) { 321 | message(Crit, msg, location); 322 | } 323 | 324 | //------------------------------------------------------------------------------ 325 | // Name: alert 326 | //------------------------------------------------------------------------------ 327 | inline void alert(std::string_view msg, const detail::sl &location = detail::sl::current()) { 328 | message(Alert, msg, location); 329 | } 330 | 331 | //------------------------------------------------------------------------------ 332 | // Name: emergency 333 | //------------------------------------------------------------------------------ 334 | inline void emergency(std::string_view msg, const detail::sl &location = detail::sl::current()) { 335 | message(Emerg, msg, location); 336 | } 337 | 338 | // A class which will easily handle redirecting std::clog to the 339 | // ostream of your choosing, and properly restore it before shutdown 340 | // to avoid errors 341 | class redirector { 342 | public: 343 | redirector(std::ostream &os) { 344 | rdbuf_ = std::clog.rdbuf(); 345 | std::clog.rdbuf(os.rdbuf()); 346 | } 347 | 348 | ~redirector() { 349 | std::clog.rdbuf(rdbuf_); 350 | } 351 | 352 | private: 353 | std::streambuf *rdbuf_; 354 | }; 355 | 356 | } 357 | 358 | #endif 359 | -------------------------------------------------------------------------------- /logger/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | add_executable(cpp-utilities-logger-test 4 | test.cpp 5 | ) 6 | 7 | target_link_libraries(cpp-utilities-logger-test 8 | PRIVATE 9 | cpp-utilities::defaults 10 | cpp-utilities::logger 11 | ) 12 | 13 | add_test( 14 | NAME cpp-utilities-logger-test 15 | COMMAND $ 16 | ) 17 | 18 | set_property(TARGET cpp-utilities-logger-test PROPERTY CXX_STANDARD 20) 19 | 20 | -------------------------------------------------------------------------------- /logger/test/test.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "cpp-utilities/logger.h" 3 | 4 | int main() { 5 | logger::emergency("This is a test"); 6 | logger::alert("This is a test"); 7 | logger::critical("This is a test"); 8 | logger::error("This is a test"); 9 | logger::warning("This is a test"); 10 | logger::notice("This is a test"); 11 | logger::info("This is a test"); 12 | logger::debug("This is a test"); 13 | } 14 | -------------------------------------------------------------------------------- /performance/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | add_library(cpp-utilities-time_code INTERFACE) 4 | add_library(cpp-utilities::time_code ALIAS cpp-utilities-time_code) 5 | 6 | target_include_directories(cpp-utilities-time_code 7 | INTERFACE 8 | ${CMAKE_CURRENT_LIST_DIR}/include 9 | ) 10 | 11 | add_subdirectory(test) 12 | -------------------------------------------------------------------------------- /performance/include/cpp-utilities/time_code.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef TIME_CODE_H_ 3 | #define TIME_CODE_H_ 4 | 5 | #include 6 | 7 | template 8 | R time_code(F func) { 9 | 10 | auto then = std::chrono::system_clock::now(); 11 | 12 | for (int i = 0; i < Count; ++i) { 13 | func(); 14 | } 15 | 16 | auto now = std::chrono::system_clock::now(); 17 | auto dur = now - then; 18 | 19 | return std::chrono::duration_cast(dur); 20 | } 21 | 22 | template 23 | R time_code_once(F func) { 24 | return time_code(func); 25 | } 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /performance/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | add_executable(cpp-utilities-time_code-test 4 | test.cpp 5 | ) 6 | 7 | target_link_libraries(cpp-utilities-time_code-test 8 | PRIVATE 9 | cpp-utilities::defaults 10 | cpp-utilities::time_code 11 | ) 12 | 13 | add_test( 14 | NAME cpp-utilities-time_code-test 15 | COMMAND $ 16 | ) 17 | 18 | -------------------------------------------------------------------------------- /performance/test/test.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "cpp-utilities/time_code.h" 3 | #include 4 | #include 5 | 6 | int main() { 7 | 8 | constexpr int Count = 10; 9 | using ms = std::chrono::microseconds; 10 | 11 | auto time1 = time_code([]() { 12 | printf("Hello World\n"); 13 | }); 14 | 15 | std::cerr << "printf(\"Hello World\\n\") x 10, Took: " << time1.count() << " \xC2\xB5s to execute." << std::endl; 16 | 17 | auto time2 = time_code_once([]() { 18 | printf("Hello World\n"); 19 | }); 20 | 21 | std::cerr << "printf(\"Hello World\\n\") once, Took: " << time2.count() << " \xC2\xB5s to execute." << std::endl; 22 | 23 | } 24 | -------------------------------------------------------------------------------- /pprint/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | add_library(cpp-utilities-pprint INTERFACE) 4 | add_library(cpp-utilities::pprint ALIAS cpp-utilities-pprint) 5 | 6 | target_include_directories(cpp-utilities-pprint 7 | INTERFACE 8 | ${CMAKE_CURRENT_LIST_DIR}/include 9 | ) 10 | 11 | -------------------------------------------------------------------------------- /pprint/include/cpp-utilities/pprint.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Evan Teran 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef UTILITIES_PPRINT_HPP_ 26 | #define UTILITIES_PPRINT_HPP_ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | namespace pprint { 41 | namespace detail { 42 | 43 | template 44 | std::string to_string(const std::vector &c, int indent); 45 | template 46 | std::string to_string(const std::list &c, int indent); 47 | template 48 | std::string to_string(const std::deque &c, int indent); 49 | template 50 | std::string to_string(const std::set &c, int indent); 51 | template 52 | std::string to_string(const std::unordered_set &c, int indent); 53 | 54 | template 55 | std::string to_string(const std::unordered_map &c, int indent); 56 | template 57 | std::string to_string(const std::map &c, int indent); 58 | 59 | std::string to_string(const std::string &s, int = 0) { 60 | return "\"" + s + "\""; 61 | } 62 | 63 | std::string to_string(int n, int = 0) { 64 | return std::to_string(n); 65 | } 66 | 67 | template 68 | std::string to_string_container(const std::string &type, const C &c, int indent) { 69 | std::stringstream ss; 70 | typename C::size_type i = 0; 71 | 72 | ss << std::setw(indent * 4) << type << std::endl; 73 | ss << std::setw(indent * 4) << "(" << std::endl; 74 | ++indent; 75 | for (typename C::const_iterator it = c.begin(); it != c.end(); ++it) { 76 | ss << std::setw(indent * 4) << '[' << i++ << "] => " << to_string(*it, indent) << std::endl; 77 | } 78 | --indent; 79 | ss << std::setw(indent * 4) << ")"; 80 | return ss.str(); 81 | } 82 | 83 | template 84 | std::string to_string_assoc_container(const std::string &type, const C &c, int indent) { 85 | std::stringstream ss; 86 | 87 | ss << std::setw(indent * 4) << type << std::endl; 88 | ss << std::setw(indent * 4) << "(" << std::endl; 89 | ++indent; 90 | for (typename C::const_iterator it = c.begin(); it != c.end(); ++it) { 91 | ss << std::setw(indent * 4) << '[' << to_string(it->first) << "] => " << to_string(it->second, indent) << std::endl; 92 | } 93 | --indent; 94 | ss << std::setw(indent * 4) << ")"; 95 | return ss.str(); 96 | } 97 | 98 | template 99 | std::string to_string(const std::vector &c, int indent) { 100 | return to_string_container("std::vector<>", c, indent); 101 | } 102 | 103 | template 104 | std::string to_string(const std::list &c, int indent) { 105 | return to_string_container("std::list<>", c, indent); 106 | } 107 | 108 | template 109 | std::string to_string(const std::deque &c, int indent) { 110 | return to_string_container("std::deque<>", c, indent); 111 | } 112 | 113 | template 114 | std::string to_string(const std::map &c, int indent) { 115 | return to_string_assoc_container("std::map<>", c, indent); 116 | } 117 | 118 | template 119 | std::string to_string(const std::unordered_map &c, int indent) { 120 | return to_string_assoc_container("std::unordered_map<>", c, indent); 121 | } 122 | 123 | template 124 | std::string to_string(const std::set &c, int indent) { 125 | return to_string_container("std::set<>", c, indent); 126 | } 127 | 128 | template 129 | std::string to_string(const std::unordered_set &c, int indent) { 130 | return to_string_container("std::unordered_set<>", c, indent); 131 | } 132 | 133 | } 134 | 135 | template 136 | std::string to_string(const std::vector &c) { 137 | return detail::to_string(c, 0); 138 | } 139 | 140 | template 141 | std::string to_string(const std::list &c) { 142 | return detail::to_string(c, 0); 143 | } 144 | 145 | template 146 | std::string to_string(const std::deque &c) { 147 | return detail::to_string(c, 0); 148 | } 149 | 150 | template 151 | std::string to_string(const std::set &c) { 152 | return detail::to_string(c, 0); 153 | } 154 | 155 | template 156 | std::string to_string(const std::unordered_set &c) { 157 | return detail::to_string(c, 0); 158 | } 159 | 160 | template 161 | std::string to_string(const std::map &c) { 162 | return detail::to_string(c, 0); 163 | } 164 | 165 | template 166 | std::string to_string(const std::unordered_map &c) { 167 | return detail::to_string(c, 0); 168 | } 169 | 170 | } 171 | 172 | #endif 173 | -------------------------------------------------------------------------------- /programs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | add_executable(byte_writer 4 | byte_writer.cpp 5 | ) 6 | 7 | target_link_libraries(byte_writer 8 | PRIVATE 9 | cpp-utilities::defaults 10 | ) 11 | -------------------------------------------------------------------------------- /programs/byte_writer.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | namespace { 7 | constexpr int Columns = 16; 8 | } 9 | 10 | int main(int argc, char *argv[]) { 11 | 12 | if (argc != 2) { 13 | std::cerr << "usage: " << argv[0] << " \n"; 14 | return -1; 15 | } 16 | 17 | std::ifstream file(argv[1]); 18 | 19 | char ch; 20 | size_t index = 0; 21 | 22 | while (file.get(ch)) { 23 | if (index == 0) { 24 | putchar('"'); 25 | } 26 | 27 | printf("\\x%02x", static_cast(ch) & 0xff); 28 | 29 | index = (index + 1) % Columns; 30 | 31 | if (index == 0) { 32 | putchar('"'); 33 | putchar('\n'); 34 | } 35 | } 36 | 37 | if (index != 0) { 38 | putchar('"'); 39 | putchar('\n'); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /range/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | add_library(cpp-utilities-range INTERFACE) 4 | add_library(cpp-utilities::range ALIAS cpp-utilities-range) 5 | 6 | target_include_directories(cpp-utilities-range 7 | INTERFACE 8 | ${CMAKE_CURRENT_LIST_DIR}/include 9 | ) 10 | 11 | -------------------------------------------------------------------------------- /range/include/cpp-utilities/range.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Evan Teran 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef UTILITIES_RANGE_HPP 26 | #define UTILITIES_RANGE_HPP 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | namespace range { 34 | 35 | namespace detail { 36 | 37 | template 38 | class const_iterator { 39 | public: 40 | using iterator_category = std::bidirectional_iterator_tag; 41 | using value_type = Integer; 42 | using difference_type = std::ptrdiff_t; 43 | using pointer = const Integer; // we lie so std::reverse_iterator<> doesn't return references to temporaries 44 | using reference = const Integer; // we lie so std::reverse_iterator<> doesn't return references to temporaries 45 | 46 | public: 47 | explicit const_iterator(Integer value) 48 | : value_(value) { 49 | } 50 | 51 | const_iterator(const const_iterator &) = default; 52 | const_iterator &operator=(const const_iterator &) = default; 53 | 54 | public: 55 | bool operator!=(const const_iterator &rhs) const { return value_ != rhs.value_; } 56 | bool operator==(const const_iterator &rhs) const { return value_ == rhs.value_; } 57 | bool operator<(const const_iterator &rhs) const { return value_ < rhs.value_; } 58 | 59 | public: 60 | Integer operator*() const { return value_; } 61 | 62 | public: 63 | const_iterator &operator++() { 64 | ++value_; 65 | return *this; 66 | } 67 | 68 | const_iterator operator++(int) { 69 | const_iterator tmp(*this); 70 | ++value_; 71 | return tmp; 72 | } 73 | 74 | const_iterator &operator--() { 75 | --value_; 76 | return *this; 77 | } 78 | 79 | const_iterator operator--(int) { 80 | const_iterator tmp(*this); 81 | --value_; 82 | return tmp; 83 | } 84 | 85 | private: 86 | Integer value_; 87 | }; 88 | 89 | } 90 | 91 | template 92 | class numeric_range { 93 | public: 94 | using size_type = std::size_t; 95 | using iterator = detail::const_iterator; 96 | using const_iterator = detail::const_iterator; 97 | using reverse_iterator = std::reverse_iterator; 98 | using const_reverse_iterator = std::reverse_iterator; 99 | 100 | public: 101 | numeric_range(Integer first, Integer last) 102 | : first_(first), last_(last) { 103 | } 104 | 105 | public: 106 | const_iterator begin() const { return const_iterator(first_); } 107 | const_iterator cbegin() const { return const_iterator(first_); } 108 | const_iterator cend() const { return const_iterator(last_); } 109 | const_iterator end() const { return const_iterator(last_); } 110 | const_reverse_iterator crbegin() const { return rbegin(); } 111 | const_reverse_iterator crend() const { return rend(); } 112 | const_reverse_iterator rbegin() const { return rbegin(); } 113 | const_reverse_iterator rend() const { return rend(); } 114 | iterator begin() { return iterator(first_); } 115 | iterator end() { return iterator(last_); } 116 | reverse_iterator rbegin() { return reverse_iterator(end()); } 117 | reverse_iterator rend() { return reverse_iterator(begin()); } 118 | 119 | public: 120 | size_type size() const { return std::distance(begin(), end()); } 121 | size_type capacity() const { return size(); } 122 | bool empty() const { return size() == 0; } 123 | 124 | private: 125 | Integer first_; 126 | Integer last_; 127 | }; 128 | 129 | template 130 | numeric_range make_numeric_range(Integer first, Integer last) { 131 | static_assert(std::is_integral::value, "Only integral sequences are supported"); 132 | return numeric_range(first, last); 133 | } 134 | 135 | } 136 | 137 | #endif 138 | -------------------------------------------------------------------------------- /string/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | add_library(cpp-utilities-string INTERFACE) 4 | add_library(cpp-utilities::string ALIAS cpp-utilities-string) 5 | 6 | target_include_directories(cpp-utilities-string 7 | INTERFACE 8 | ${CMAKE_CURRENT_LIST_DIR}/include 9 | ) 10 | 11 | add_subdirectory(test) 12 | 13 | -------------------------------------------------------------------------------- /string/include/cpp-utilities/string.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Evan Teran 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef UTILITIES_STRING_HPP_ 26 | #define UTILITIES_STRING_HPP_ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | namespace string { 38 | 39 | //------------------------------------------------------------------------------ 40 | // Name: safe_ctype 41 | //------------------------------------------------------------------------------ 42 | template 43 | int safe_ctype(unsigned char c) { 44 | return F(c); 45 | } 46 | 47 | //------------------------------------------------------------------------------ 48 | // Name: ends_with 49 | //------------------------------------------------------------------------------ 50 | inline bool ends_with(const std::string &s, char ch) { 51 | return !s.empty() && s.back() == ch; 52 | } 53 | 54 | //------------------------------------------------------------------------------ 55 | // Name: ends_with 56 | //------------------------------------------------------------------------------ 57 | inline bool ends_with(const std::string &s, const std::string &suffix) { 58 | return std::mismatch(suffix.rbegin(), suffix.rend(), s.rbegin()).first == suffix.rend(); 59 | } 60 | 61 | //------------------------------------------------------------------------------ 62 | // Name: starts_with 63 | //------------------------------------------------------------------------------ 64 | inline bool starts_with(const std::string &s, char ch) { 65 | return !s.empty() && s.front() == ch; 66 | } 67 | 68 | //------------------------------------------------------------------------------ 69 | // Name: starts_with 70 | //------------------------------------------------------------------------------ 71 | inline bool starts_with(const std::string &s, const std::string &prefix) { 72 | return std::mismatch(prefix.begin(), prefix.end(), s.begin()).first == prefix.end(); 73 | } 74 | 75 | //------------------------------------------------------------------------------ 76 | // Name: rtrim 77 | //------------------------------------------------------------------------------ 78 | inline void rtrim(std::string &s) { 79 | s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { 80 | return !safe_ctype(ch); 81 | }).base(), 82 | s.end()); 83 | } 84 | 85 | //------------------------------------------------------------------------------ 86 | // Name: ltrim 87 | //------------------------------------------------------------------------------ 88 | inline void ltrim(std::string &s) { 89 | s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) { 90 | return !safe_ctype(ch); 91 | })); 92 | } 93 | 94 | //------------------------------------------------------------------------------ 95 | // Name: trim 96 | //------------------------------------------------------------------------------ 97 | inline void trim(std::string &s) { 98 | ltrim(s); 99 | rtrim(s); 100 | } 101 | 102 | //------------------------------------------------------------------------------ 103 | // Name: rtrim_copy 104 | //------------------------------------------------------------------------------ 105 | inline std::string rtrim_copy(std::string s) { 106 | rtrim(s); 107 | return s; 108 | } 109 | 110 | //------------------------------------------------------------------------------ 111 | // Name: ltrim_copy 112 | //------------------------------------------------------------------------------ 113 | inline std::string ltrim_copy(std::string s) { 114 | ltrim(s); 115 | return s; 116 | } 117 | 118 | //------------------------------------------------------------------------------ 119 | // Name: trim_copy 120 | //------------------------------------------------------------------------------ 121 | inline std::string trim_copy(std::string &s) { 122 | trim(s); 123 | return s; 124 | } 125 | 126 | //------------------------------------------------------------------------------ 127 | // Name: toupper 128 | //------------------------------------------------------------------------------ 129 | inline void toupper(std::string &s) { 130 | std::transform(s.begin(), s.end(), s.begin(), [](int ch) { 131 | return std::toupper(ch); 132 | }); 133 | } 134 | 135 | //------------------------------------------------------------------------------ 136 | // Name: tolower 137 | //------------------------------------------------------------------------------ 138 | inline void tolower(std::string &s) { 139 | std::transform(s.begin(), s.end(), s.begin(), [](int ch) { 140 | return std::tolower(ch); 141 | }); 142 | } 143 | 144 | //------------------------------------------------------------------------------ 145 | // Name: toupper_copy 146 | //------------------------------------------------------------------------------ 147 | inline std::string toupper_copy(std::string s) { 148 | toupper(s); 149 | return s; 150 | } 151 | 152 | //------------------------------------------------------------------------------ 153 | // Name: tolower_copy 154 | //------------------------------------------------------------------------------ 155 | inline std::string tolower_copy(std::string s) { 156 | tolower(s); 157 | return s; 158 | } 159 | 160 | //------------------------------------------------------------------------------ 161 | // Name: implode 162 | // Desc: join elements with a char 163 | //------------------------------------------------------------------------------ 164 | inline std::string implode(char glue, const std::vector &pieces) { 165 | std::string s; 166 | if (!pieces.empty()) { 167 | s.append(pieces[0]); 168 | for (size_t i = 1; i < pieces.size(); ++i) { 169 | s.push_back(glue); 170 | s.append(pieces[i]); 171 | } 172 | } 173 | return s; 174 | } 175 | 176 | //------------------------------------------------------------------------------ 177 | // Name: implode 178 | // Desc: join elements with a string 179 | //------------------------------------------------------------------------------ 180 | inline std::string implode(const std::string &glue, const std::vector &pieces) { 181 | std::string s; 182 | if (!pieces.empty()) { 183 | s.append(pieces[0]); 184 | for (size_t i = 1; i < pieces.size(); ++i) { 185 | s.append(glue); 186 | s.append(pieces[i]); 187 | } 188 | } 189 | return s; 190 | } 191 | 192 | //------------------------------------------------------------------------------ 193 | // Name: explode 194 | //------------------------------------------------------------------------------ 195 | inline std::vector explode(const std::string &delimeter, const std::string &string, int limit) { 196 | std::vector r; 197 | 198 | if (!string.empty()) { 199 | if (limit >= 0) { 200 | if (limit == 0) { 201 | limit = 1; 202 | } 203 | 204 | size_t first = 0; 205 | size_t last = string.find(delimeter); 206 | 207 | while (last != std::string::npos) { 208 | 209 | if (--limit == 0) { 210 | break; 211 | } 212 | 213 | r.emplace_back(string.substr(first, last - first)); 214 | first = last + delimeter.size(); 215 | last = string.find(delimeter, last + delimeter.size()); 216 | } 217 | 218 | r.emplace_back(string.substr(first)); 219 | } else { 220 | size_t first = 0; 221 | size_t last = string.find(delimeter); 222 | 223 | while (last != std::string::npos) { 224 | r.emplace_back(string.substr(first, last - first)); 225 | first = last + delimeter.size(); 226 | last = string.find(delimeter, last + delimeter.size()); 227 | } 228 | 229 | r.emplace_back(string.substr(first)); 230 | 231 | while (limit < 0) { 232 | r.pop_back(); 233 | ++limit; 234 | } 235 | } 236 | } 237 | 238 | return r; 239 | } 240 | 241 | //------------------------------------------------------------------------------ 242 | // Name: explode 243 | //------------------------------------------------------------------------------ 244 | inline std::vector explode(const std::string &delimeter, const std::string &string) { 245 | return explode(delimeter, string, std::numeric_limits::max()); 246 | } 247 | 248 | //------------------------------------------------------------------------------ 249 | // Name: explode 250 | //------------------------------------------------------------------------------ 251 | inline std::vector explode(char delimeter, const std::string &string, int limit) { 252 | std::vector r; 253 | 254 | if (!string.empty()) { 255 | if (limit >= 0) { 256 | if (limit == 0) { 257 | limit = 1; 258 | } 259 | 260 | size_t first = 0; 261 | size_t last = string.find(delimeter); 262 | 263 | while (last != std::string::npos) { 264 | 265 | if (--limit == 0) { 266 | break; 267 | } 268 | 269 | r.emplace_back(string.substr(first, last - first)); 270 | first = last + 1; 271 | last = string.find(delimeter, last + 1); 272 | } 273 | 274 | r.emplace_back(string.substr(first)); 275 | } else { 276 | size_t first = 0; 277 | size_t last = string.find(delimeter); 278 | 279 | while (last != std::string::npos) { 280 | r.emplace_back(string.substr(first, last - first)); 281 | first = last + 1; 282 | last = string.find(delimeter, last + 1); 283 | } 284 | 285 | r.emplace_back(string.substr(first)); 286 | 287 | while (limit < 0) { 288 | r.pop_back(); 289 | ++limit; 290 | } 291 | } 292 | } 293 | 294 | return r; 295 | } 296 | 297 | //------------------------------------------------------------------------------ 298 | // Name: explode 299 | //------------------------------------------------------------------------------ 300 | inline std::vector explode(char delimeter, const std::string &string) { 301 | return explode(delimeter, string, std::numeric_limits::max()); 302 | } 303 | 304 | //------------------------------------------------------------------------------ 305 | // Name: split 306 | //------------------------------------------------------------------------------ 307 | template 308 | void split(const std::string &s, char delim, Op op) { 309 | std::stringstream ss(s); 310 | for (std::string item; std::getline(ss, item, delim);) { 311 | *op++ = item; 312 | } 313 | } 314 | 315 | //------------------------------------------------------------------------------ 316 | // Name: split 317 | //------------------------------------------------------------------------------ 318 | inline std::vector split(const std::string &s, char delim) { 319 | std::vector elems; 320 | split(s, delim, std::back_inserter(elems)); 321 | return elems; 322 | } 323 | 324 | } 325 | 326 | #endif 327 | -------------------------------------------------------------------------------- /string/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | add_executable(cpp-utilities-string-test 4 | test.cpp 5 | ) 6 | 7 | target_link_libraries(cpp-utilities-string-test 8 | PRIVATE 9 | cpp-utilities::defaults 10 | cpp-utilities::string 11 | ) 12 | 13 | set_property(TARGET cpp-utilities-string-test PROPERTY CXX_STANDARD 14) 14 | 15 | 16 | add_test( 17 | NAME cpp-utilities-string-test 18 | COMMAND $ 19 | ) 20 | 21 | -------------------------------------------------------------------------------- /string/test/test.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "cpp-utilities/string.h" 3 | #include 4 | 5 | int main() { 6 | std::string s1 = "\n hello world \f "; 7 | assert(string::rtrim_copy(s1) == "\n hello world"); 8 | assert(string::ltrim_copy(s1) == "hello world \f "); 9 | assert(string::trim_copy(s1) == "hello world"); 10 | } 11 | -------------------------------------------------------------------------------- /thread_pool/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | add_library(cpp-utilities-thread_pool INTERFACE) 4 | add_library(cpp-utilities::thread_pool ALIAS cpp-utilities-thread_pool) 5 | 6 | target_include_directories(cpp-utilities-thread_pool 7 | INTERFACE 8 | ${CMAKE_CURRENT_LIST_DIR}/include 9 | ) 10 | 11 | 12 | add_subdirectory(test) 13 | 14 | -------------------------------------------------------------------------------- /thread_pool/include/cpp-utilities/thread_pool.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef THREAD_POOL_H_ 3 | #define THREAD_POOL_H_ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | class thread_pool { 15 | public: 16 | using work_type = std::function; 17 | 18 | public: 19 | /** 20 | * Creates the thread pool with N threads where N is the value of 21 | * std::thread::hardware_concurrency() 22 | */ 23 | thread_pool() 24 | : thread_pool(std::thread::hardware_concurrency()) { 25 | } 26 | 27 | /** 28 | * Creates the thread pool with threads 29 | * 30 | * @param count The number of threads in the pool 31 | */ 32 | thread_pool(std::size_t count) { 33 | // create all the threads 34 | for (std::size_t i = 0; i < count; ++i) { 35 | threads_.emplace_back([this]() { 36 | // keep looking for more tasks until we suicide 37 | while (true) { 38 | 39 | // get a new worker, this'll block while the queue is empty 40 | work_type worker = get_worker(); 41 | 42 | // special case? 43 | if (!worker) { 44 | this->add_worker(nullptr); 45 | break; 46 | } else { 47 | // do the work 48 | worker(); 49 | } 50 | } 51 | }); 52 | } 53 | } 54 | 55 | /** 56 | * Destroys the thread pool, waits still all outstanding work is complete 57 | */ 58 | ~thread_pool() { 59 | // add special token which tells all the threads to suicide 60 | add_worker(nullptr); 61 | 62 | // wait till all outstanding tasks are done 63 | for (std::thread &thread : threads_) { 64 | thread.join(); 65 | } 66 | } 67 | 68 | public: 69 | /** 70 | * Adds a new work item to the pool for execution 71 | * 72 | * @param worker the work to do 73 | */ 74 | void add_worker(work_type worker) { 75 | { 76 | std::lock_guard lock(queue_lock_); 77 | work_queue_.push(std::move(worker)); 78 | } 79 | queue_condition_.notify_one(); 80 | } 81 | 82 | /** 83 | * Waits until there is at least one work item to do, and then returns 84 | * the work item after popping it off the queue 85 | * 86 | * @return 87 | */ 88 | work_type get_worker() { 89 | std::unique_lock lock(queue_lock_); 90 | queue_condition_.wait(lock, [this]() { 91 | return !work_queue_.empty(); 92 | }); 93 | 94 | work_type val = std::move(work_queue_.front()); 95 | work_queue_.pop(); 96 | return val; 97 | } 98 | 99 | private: 100 | std::vector threads_; 101 | std::queue work_queue_; 102 | std::mutex queue_lock_; 103 | std::condition_variable queue_condition_; 104 | }; 105 | 106 | #endif 107 | -------------------------------------------------------------------------------- /thread_pool/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | add_executable(cpp-utilities-thread_pool-test 4 | thread_pool.cpp 5 | ) 6 | 7 | find_package(Threads REQUIRED) 8 | 9 | target_link_libraries(cpp-utilities-thread_pool-test 10 | PRIVATE 11 | cpp-utilities::defaults 12 | cpp-utilities::thread_pool 13 | Threads::Threads 14 | ) 15 | 16 | add_test( 17 | NAME cpp-utilities-thread_pool-test 18 | COMMAND $ 19 | ) 20 | 21 | -------------------------------------------------------------------------------- /thread_pool/test/thread_pool.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() { 5 | thread_pool pool; 6 | } 7 | -------------------------------------------------------------------------------- /uint128/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | add_library(cpp-utilities-uint128 INTERFACE) 4 | add_library(cpp-utilities::uint128 ALIAS cpp-utilities-uint128) 5 | 6 | target_include_directories(cpp-utilities-uint128 7 | INTERFACE 8 | ${CMAKE_CURRENT_LIST_DIR}/include 9 | ) 10 | 11 | add_subdirectory(test) 12 | 13 | -------------------------------------------------------------------------------- /uint128/include/cpp-utilities/uint128.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Evan Teran 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef UINT128_20050119_H_ 26 | #define UINT128_20050119_H_ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include 35 | 36 | namespace numeric { 37 | 38 | namespace detail { 39 | 40 | template 41 | static void divide(const T &numerator, const T &denominator, T "ient, T &remainder) { 42 | 43 | static const int bits = sizeof(T) * CHAR_BIT; 44 | 45 | if (denominator == 0) { 46 | throw std::domain_error("divide by zero"); 47 | } else { 48 | T n = numerator; 49 | T d = denominator; 50 | T x = 1; 51 | T answer = 0; 52 | 53 | while ((n >= d) && (((d >> (bits - 1)) & 1) == 0)) { 54 | x <<= 1; 55 | d <<= 1; 56 | } 57 | 58 | while (x != 0) { 59 | if (n >= d) { 60 | n -= d; 61 | answer |= x; 62 | } 63 | 64 | x >>= 1; 65 | d >>= 1; 66 | } 67 | 68 | quotient = answer; 69 | remainder = n; 70 | } 71 | } 72 | } 73 | 74 | // convenience macro 75 | #define U128_C(s) uint128(#s) 76 | 77 | class uint128 : public boost::shiftable>>>> { 78 | public: 79 | typedef uint64_t base_type; 80 | 81 | public: 82 | static const unsigned int size = (sizeof(base_type) + sizeof(base_type)) * CHAR_BIT; 83 | 84 | private: 85 | base_type lo; 86 | base_type hi; 87 | 88 | public: 89 | // constructors for all basic types 90 | uint128() 91 | : lo(0), hi(0) { 92 | } 93 | 94 | uint128(int value) 95 | : lo(static_cast(value)), hi(0) { 96 | if (value < 0) 97 | hi = static_cast(-1); 98 | } 99 | 100 | uint128(unsigned int value) 101 | : lo(static_cast(value)), hi(0) { 102 | } 103 | 104 | uint128(float value) 105 | : lo(static_cast(value)), hi(0) { 106 | } 107 | 108 | uint128(double value) 109 | : lo(static_cast(value)), hi(0) { 110 | } 111 | 112 | uint128(const uint128 &value) 113 | : lo(value.lo), hi(value.hi) { 114 | } 115 | 116 | uint128(base_type value) 117 | : lo(value), hi(0) { 118 | } 119 | 120 | uint128(const std::string &sz) 121 | : lo(0), hi(0) { 122 | 123 | // do we have at least one character? 124 | if (!sz.empty()) { 125 | // make some reasonable assumptions 126 | int radix = 10; 127 | bool minus = false; 128 | 129 | auto i = sz.begin(); 130 | 131 | // check for minus sign, i suppose technically this should only apply 132 | // to base 10, but who says that -0x1 should be invalid? 133 | if (*i == '-') { 134 | ++i; 135 | minus = true; 136 | } 137 | 138 | // check if there is radix changing prefix (0 or 0x) 139 | if (i != sz.end()) { 140 | if (*i == '0') { 141 | radix = 8; 142 | ++i; 143 | if (i != sz.end()) { 144 | if (*i == 'x') { 145 | radix = 16; 146 | ++i; 147 | } 148 | } 149 | } 150 | 151 | while (i != sz.end()) { 152 | unsigned int n; 153 | const char ch = *i; 154 | 155 | if (ch >= 'A' && ch <= 'Z') { 156 | if (((ch - 'A') + 10) < radix) { 157 | n = (ch - 'A') + 10; 158 | } else { 159 | break; 160 | } 161 | } else if (ch >= 'a' && ch <= 'z') { 162 | if (((ch - 'a') + 10) < radix) { 163 | n = (ch - 'a') + 10; 164 | } else { 165 | break; 166 | } 167 | } else if (ch >= '0' && ch <= '9') { 168 | if ((ch - '0') < radix) { 169 | n = (ch - '0'); 170 | } else { 171 | break; 172 | } 173 | } else { 174 | /* completely invalid character */ 175 | break; 176 | } 177 | 178 | (*this) *= radix; 179 | (*this) += n; 180 | 181 | ++i; 182 | } 183 | } 184 | 185 | // if this was a negative number, do that two's compliment madness :-P 186 | if (minus) { 187 | *this = -*this; 188 | } 189 | } 190 | } 191 | 192 | uint128 &operator=(const uint128 &other) { 193 | if (&other != this) { 194 | lo = other.lo; 195 | hi = other.hi; 196 | } 197 | return *this; 198 | } 199 | 200 | public: // comparison operators 201 | bool operator==(const uint128 &o) const { 202 | return hi == o.hi && lo == o.lo; 203 | } 204 | 205 | bool operator<(const uint128 &o) const { 206 | return (hi == o.hi) ? lo < o.lo : hi < o.hi; 207 | } 208 | 209 | public: // unary operators 210 | bool operator!() const { 211 | return !(hi != 0 || lo != 0); 212 | } 213 | 214 | uint128 operator-() const { 215 | // standard 2's compliment negation 216 | return ~uint128(*this) + 1; 217 | } 218 | 219 | uint128 operator~() const { 220 | uint128 t(*this); 221 | t.lo = ~t.lo; 222 | t.hi = ~t.hi; 223 | return t; 224 | } 225 | 226 | uint128 &operator++() { 227 | if (++lo == 0) { 228 | ++hi; 229 | } 230 | return *this; 231 | } 232 | 233 | uint128 &operator--() { 234 | if (lo-- == 0) { 235 | --hi; 236 | } 237 | return *this; 238 | } 239 | 240 | public: // basic math operators 241 | uint128 &operator+=(const uint128 &b) { 242 | const base_type old_lo = lo; 243 | 244 | lo += b.lo; 245 | hi += b.hi; 246 | 247 | if (lo < old_lo) { 248 | ++hi; 249 | } 250 | 251 | return *this; 252 | } 253 | 254 | uint128 &operator-=(const uint128 &b) { 255 | // it happens to be way easier to write it 256 | // this way instead of make a subtraction algorithm 257 | return *this += -b; 258 | } 259 | 260 | uint128 &operator*=(const uint128 &b) { 261 | 262 | // check for multiply by 0 263 | // result is always 0 :-P 264 | if (b == 0) { 265 | hi = 0; 266 | lo = 0; 267 | } else if (b != 1) { 268 | 269 | // check we aren't multiplying by 1 270 | 271 | uint128 a(*this); 272 | uint128 t = b; 273 | 274 | lo = 0; 275 | hi = 0; 276 | 277 | for (unsigned int i = 0; i < size; ++i) { 278 | if ((t & 1) != 0) { 279 | *this += (a << i); 280 | } 281 | 282 | t >>= 1; 283 | } 284 | } 285 | 286 | return *this; 287 | } 288 | 289 | uint128 &operator|=(const uint128 &b) { 290 | hi |= b.hi; 291 | lo |= b.lo; 292 | return *this; 293 | } 294 | 295 | uint128 &operator&=(const uint128 &b) { 296 | hi &= b.hi; 297 | lo &= b.lo; 298 | return *this; 299 | } 300 | 301 | uint128 &operator^=(const uint128 &b) { 302 | hi ^= b.hi; 303 | lo ^= b.lo; 304 | return *this; 305 | } 306 | 307 | uint128 &operator/=(const uint128 &b) { 308 | uint128 remainder; 309 | detail::divide(*this, b, *this, remainder); 310 | return *this; 311 | } 312 | 313 | uint128 &operator%=(const uint128 &b) { 314 | uint128 quotient; 315 | detail::divide(*this, b, quotient, *this); 316 | return *this; 317 | } 318 | 319 | uint128 &operator<<=(const uint128 &rhs) { 320 | 321 | unsigned int n = rhs.to_integer(); 322 | 323 | if (n >= size) { 324 | hi = 0; 325 | lo = 0; 326 | } else { 327 | const unsigned int half_size = size / 2; 328 | 329 | if (n >= half_size) { 330 | n -= half_size; 331 | hi = lo; 332 | lo = 0; 333 | } 334 | 335 | if (n != 0) { 336 | // shift high half 337 | hi <<= n; 338 | 339 | const base_type mask(~(base_type(-1) >> n)); 340 | 341 | // and add them to high half 342 | hi |= (lo & mask) >> (half_size - n); 343 | 344 | // and finally shift also low half 345 | lo <<= n; 346 | } 347 | } 348 | 349 | return *this; 350 | } 351 | 352 | uint128 &operator>>=(const uint128 &rhs) { 353 | 354 | unsigned int n = rhs.to_integer(); 355 | 356 | if (n >= size) { 357 | hi = 0; 358 | lo = 0; 359 | } else { 360 | const unsigned int half_size = size / 2; 361 | 362 | if (n >= half_size) { 363 | n -= half_size; 364 | lo = hi; 365 | hi = 0; 366 | } 367 | 368 | if (n != 0) { 369 | // shift low half 370 | lo >>= n; 371 | 372 | // get lower N bits of high half 373 | const base_type mask(~(base_type(-1) << n)); 374 | 375 | // and add them to low qword 376 | lo |= (hi & mask) << (half_size - n); 377 | 378 | // and finally shift also high half 379 | hi >>= n; 380 | } 381 | } 382 | 383 | return *this; 384 | } 385 | 386 | public: 387 | int to_integer() const { 388 | return static_cast(lo); 389 | } 390 | 391 | base_type to_base_type() const { 392 | return lo; 393 | } 394 | 395 | std::string to_string(unsigned int radix = 10) const { 396 | if (*this == 0) { 397 | return "0"; 398 | } 399 | 400 | if (radix < 2 || radix > 37) { 401 | return "(invalid radix)"; 402 | } 403 | 404 | // at worst it will be size digits (base 2) so make our buffer 405 | // that plus room for null terminator 406 | static char sz[size + 1]; 407 | sz[sizeof(sz) - 1] = '\0'; 408 | 409 | uint128 ii(*this); 410 | int i = size - 1; 411 | 412 | while (ii != 0 && i) { 413 | 414 | uint128 remainder; 415 | detail::divide(ii, uint128(radix), ii, remainder); 416 | sz[--i] = "0123456789abcdefghijklmnopqrstuvwxyz"[remainder.to_integer()]; 417 | } 418 | 419 | return &sz[i]; 420 | } 421 | }; 422 | 423 | std::ostream &operator<<(std::ostream &o, const uint128 &n) { 424 | switch (o.flags() & (std::ios_base::hex | std::ios_base::dec | std::ios_base::oct)) { 425 | case std::ios_base::hex: 426 | o << n.to_string(16); 427 | break; 428 | case std::ios_base::dec: 429 | o << n.to_string(10); 430 | break; 431 | case std::ios_base::oct: 432 | o << n.to_string(8); 433 | break; 434 | default: 435 | o << n.to_string(); 436 | break; 437 | } 438 | return o; 439 | } 440 | 441 | typedef uint128 uint128_t; 442 | } 443 | 444 | #endif 445 | -------------------------------------------------------------------------------- /uint128/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | add_executable(cpp-utilities-uint128-test 4 | uint128.cpp 5 | ) 6 | 7 | find_package(Boost REQUIRED) 8 | 9 | 10 | target_link_libraries(cpp-utilities-uint128-test 11 | PRIVATE 12 | cpp-utilities::defaults 13 | cpp-utilities::uint128 14 | Boost::boost 15 | ) 16 | 17 | add_test( 18 | NAME cpp-utilities-uint128-test 19 | COMMAND $ 20 | ) 21 | 22 | -------------------------------------------------------------------------------- /uint128/test/uint128.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using numeric::uint128_t; 5 | using numeric::uint128; 6 | 7 | int main() { 8 | 9 | uint128_t x = uint128_t(0xaabbccdd11223344); 10 | uint128_t y = uint128_t(0xaabbccdd11223344); 11 | 12 | 13 | std::cout << "-----------------" << std::endl; 14 | uint128_t z = x * y; 15 | std::cout << "-----------------" << std::endl; 16 | std::cout << std::hex << z << std::endl; 17 | 18 | 19 | } 20 | -------------------------------------------------------------------------------- /uuid/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | add_library(cpp-utilities-uuid INTERFACE) 4 | add_library(cpp-utilities::uuid ALIAS cpp-utilities-uuid) 5 | 6 | target_include_directories(cpp-utilities-uuid 7 | INTERFACE 8 | ${CMAKE_CURRENT_LIST_DIR}/include 9 | ) 10 | 11 | target_link_libraries(cpp-utilities-uuid 12 | INTERFACE 13 | cpp-utilities::hash 14 | ) 15 | 16 | add_subdirectory(test) 17 | 18 | -------------------------------------------------------------------------------- /uuid/include/cpp-utilities/uuid.h: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Evan Teran 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef UUID_H_ 26 | #define UUID_H_ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | class uuid { 36 | public: 37 | uuid() = default; 38 | uuid(const uuid &) = default; 39 | uuid &operator=(const uuid &) = default; 40 | 41 | public: 42 | static uuid from_string(std::string str) noexcept { 43 | 44 | assert(is_valid(str)); 45 | 46 | // once we know it's valid, we can just strip out the optional 47 | // stuff we don't really need 48 | str.erase(std::remove(str.begin(), str.end(), '{'), str.end()); 49 | str.erase(std::remove(str.begin(), str.end(), '}'), str.end()); 50 | str.erase(std::remove(str.begin(), str.end(), '-'), str.end()); 51 | 52 | assert(str.size() == 32); 53 | 54 | uuid r; 55 | 56 | for (size_t i = 0; i < 16; ++i) { 57 | const char ch[3] = {str[i * 2], str[i * 2 + 1], '\0'}; 58 | const auto v = static_cast(strtoul(ch, nullptr, 16)); 59 | r.v_[i] = v; 60 | } 61 | 62 | return r; 63 | } 64 | 65 | static uuid v3(const uuid &ns, const std::string &name) noexcept { 66 | 67 | // To determine the version 3 uuid of a given name, the uuid of the namespace (e.g., 6ba7b810-9dad-11d1-80b4-00c04fd430c8 for a domain) 68 | // is transformed to a string of bytes corresponding to its hexadecimal digits, concatenated with the input name, hashed with 69 | // MD5 yielding 128 bits. Six bits are replaced by fixed values, four of these bits indicate the version, 0011 for version 3. Finally, 70 | // the fixed hash is transformed back into the hexadecimal form with hyphens separating the parts relevant in other uuid versions. 71 | 72 | uuid r; 73 | 74 | std::vector bin; 75 | bin.reserve(16 + name.size()); 76 | 77 | for (size_t i = 0; i < 16; ++i) { 78 | bin.push_back(ns[i]); 79 | } 80 | 81 | for (char ch : name) { 82 | bin.push_back(static_cast(ch)); 83 | } 84 | 85 | auto digest = hash::md5(bin.begin(), bin.end()).finalize(); 86 | auto bytes = digest.bytes(); 87 | 88 | for (size_t i = 0; i < 16; ++i) { 89 | r.v_[i] = bytes[i]; 90 | } 91 | 92 | // ensure correctness 93 | r.v_[6] = (r.v_[6] & 0x0f) | 0x30; // make version 3 94 | r.v_[8] = (r.v_[8] & 0x3f) | 0x80; // make variant 1 95 | 96 | return r; 97 | } 98 | 99 | static uuid v4() noexcept { 100 | uuid r; 101 | 102 | std::random_device rd; 103 | std::mt19937 gen(rd()); 104 | std::uniform_int_distribution<> dis(0x00, 0xff); 105 | 106 | r.v_[0] = static_cast(dis(gen)); 107 | r.v_[1] = static_cast(dis(gen)); 108 | r.v_[2] = static_cast(dis(gen)); 109 | r.v_[3] = static_cast(dis(gen)); 110 | r.v_[4] = static_cast(dis(gen)); 111 | r.v_[5] = static_cast(dis(gen)); 112 | r.v_[6] = static_cast((dis(gen) & 0x0f) | 0x40); // make version 4 113 | r.v_[7] = static_cast(dis(gen)); 114 | r.v_[8] = static_cast((dis(gen) & 0x3f) | 0x80); // make variant 1 115 | r.v_[9] = static_cast(dis(gen)); 116 | r.v_[10] = static_cast(dis(gen)); 117 | r.v_[11] = static_cast(dis(gen)); 118 | r.v_[12] = static_cast(dis(gen)); 119 | r.v_[13] = static_cast(dis(gen)); 120 | r.v_[14] = static_cast(dis(gen)); 121 | r.v_[15] = static_cast(dis(gen)); 122 | 123 | return r; 124 | } 125 | 126 | static uuid v5(const uuid &ns, const std::string &name) noexcept { 127 | 128 | // To determine the version 5 uuid of a given name, the uuid of the namespace (e.g., 6ba7b810-9dad-11d1-80b4-00c04fd430c8 for a domain) 129 | // is transformed to a string of bytes corresponding to its hexadecimal digits, concatenated with the input name, hashed with 130 | // sha1 yielding 160 bits. Six bits are replaced by fixed values, four of these bits indicate the version, 0011 for version 5. Finally, 131 | // the first 128 bits of the fixed hash is transformed back into the hexadecimal form with hyphens separating the parts relevant in other uuid versions. 132 | 133 | uuid r; 134 | 135 | std::vector bin; 136 | bin.reserve(16 + name.size()); 137 | 138 | for (size_t i = 0; i < 16; ++i) { 139 | bin.push_back(ns[i]); 140 | } 141 | 142 | for (char ch : name) { 143 | bin.push_back(static_cast(ch)); 144 | } 145 | 146 | auto digest = hash::sha1(bin.begin(), bin.end()).finalize(); 147 | auto bytes = digest.bytes(); 148 | 149 | for (size_t i = 0; i < 16; ++i) { 150 | r.v_[i] = bytes[i]; 151 | } 152 | 153 | // ensure correctness 154 | r.v_[6] = (r.v_[6] & 0x0f) | 0x50; // make version 5 155 | r.v_[8] = (r.v_[8] & 0x3f) | 0x80; // make variant 1 156 | 157 | return r; 158 | } 159 | 160 | static bool is_valid(const std::string &uuid) noexcept { 161 | 162 | bool has_braces = false; 163 | bool has_dashes = false; 164 | int digit_index = 0; 165 | 166 | auto it = uuid.begin(); 167 | 168 | // first test to see if we are dealing with a brace wrapped string 169 | // if so, consume the brace and note it 170 | if (it != uuid.end()) { 171 | if (*it == '{') { 172 | has_braces = true; 173 | ++it; 174 | } 175 | } 176 | 177 | // consume digits as we go 178 | while (it != uuid.end()) { 179 | 180 | // at the very least, we expect a hex digit 181 | if (!isxdigit(*it)) { 182 | break; 183 | } 184 | 185 | // digits 12 and 16 have specific valid values 186 | if (digit_index == 12) { 187 | const char *p = strchr("12345", *it); 188 | if (!p) { 189 | return false; 190 | } 191 | } else if (digit_index == 16) { 192 | const char *p = strchr("89abAB", *it); 193 | if (!p) { 194 | return false; 195 | } 196 | } 197 | 198 | ++digit_index; 199 | ++it; 200 | 201 | // if we are at digit 8, then we test to see if there 202 | // is a dash, if we see one, we want one at 12,16,20 too 203 | if (it != uuid.end()) { 204 | switch (digit_index) { 205 | case 8: 206 | if (*it == '-') { 207 | has_dashes = true; 208 | ++it; 209 | } 210 | break; 211 | case 12: 212 | case 16: 213 | case 20: 214 | if (has_dashes) { 215 | if (*it == '-') { 216 | ++it; 217 | } else { 218 | return false; 219 | } 220 | } 221 | break; 222 | } 223 | } 224 | } 225 | 226 | // handle the closing brace 227 | if (digit_index == 32 && has_braces) { 228 | if (it == uuid.end()) { 229 | return false; 230 | } 231 | 232 | if (*it++ != '}') { 233 | return false; 234 | } 235 | } 236 | 237 | // did we consume the whole string and get 32 digits? 238 | return (digit_index == 32) && (it == uuid.end()); 239 | } 240 | 241 | int version() const noexcept { 242 | return (v_[6] >> 4) & 0x0f; 243 | } 244 | 245 | bool operator==(const uuid &rhs) const noexcept { 246 | return std::memcmp(v_, rhs.v_, 16) == 0; 247 | } 248 | 249 | bool operator!=(const uuid &rhs) const noexcept { 250 | return !(*this == rhs); 251 | } 252 | 253 | bool operator<(const uuid &rhs) const noexcept { 254 | return std::memcmp(v_, rhs.v_, 16) < 0; 255 | } 256 | 257 | public: 258 | bool is_null() const noexcept { 259 | return *this == uuid(); 260 | } 261 | 262 | bool is_valid() const noexcept { 263 | const int ver = version(); 264 | return ver >= 1 && ver <= 5; 265 | } 266 | 267 | public: 268 | std::string to_string() const noexcept { 269 | char buffer[37]; 270 | snprintf(buffer, sizeof(buffer), "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", 271 | v_[0], 272 | v_[1], 273 | v_[2], 274 | v_[3], 275 | v_[4], 276 | v_[5], 277 | v_[6], 278 | v_[7], 279 | v_[8], 280 | v_[9], 281 | v_[10], 282 | v_[11], 283 | v_[12], 284 | v_[13], 285 | v_[14], 286 | v_[15]); 287 | 288 | return buffer; 289 | } 290 | 291 | uint8_t operator[](size_t index) const noexcept { 292 | assert(index < sizeof(v_)); 293 | return v_[index]; 294 | } 295 | 296 | uint8_t &operator[](size_t index) noexcept { 297 | assert(index < sizeof(v_)); 298 | return v_[index]; 299 | } 300 | 301 | private: 302 | uint8_t v_[16] = {}; 303 | }; 304 | 305 | #endif 306 | -------------------------------------------------------------------------------- /uuid/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | add_executable(cpp-utilities-uuid-test 4 | uuid.cpp 5 | ) 6 | 7 | target_link_libraries(cpp-utilities-uuid-test 8 | PRIVATE 9 | cpp-utilities::defaults 10 | cpp-utilities::uuid 11 | ) 12 | 13 | add_test( 14 | NAME cpp-utilities-uuid-test 15 | COMMAND $ 16 | ) 17 | 18 | -------------------------------------------------------------------------------- /uuid/test/uuid.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Evan Teran 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | 28 | int main() { 29 | 30 | auto ns = uuid::from_string("{6ba7b810-9dad-11d1-80b4-00c04fd430c8}"); 31 | auto v3 = uuid::v3(ns, "test"); 32 | 33 | assert(v3.to_string() == "45a113ac-c7f2-30b0-90a5-a399ab912716"); 34 | 35 | auto v5 = uuid::v5(ns, "test"); 36 | assert(v5.to_string() == "4be0643f-1d98-573b-97cd-ca98a65347dd"); 37 | 38 | auto uuid1 = uuid::v4(); 39 | 40 | assert(!uuid1.is_null()); 41 | assert(uuid1.is_valid()); 42 | assert(uuid1.version() == 4); 43 | 44 | assert(uuid::is_valid(uuid1.to_string())); 45 | 46 | uuid uuid2; 47 | assert(uuid2.is_null()); 48 | assert(!uuid2.is_valid()); 49 | } 50 | --------------------------------------------------------------------------------