├── docs ├── result.jpg ├── linked_hash_map.png ├── linked_hash_set.png ├── linked_hash_map (LRU Mode).png ├── linked_hash_set (LRU Mode).png └── index.html ├── test ├── main.cpp ├── CMakeLists.txt ├── common.h ├── bench_LinkedHashSet.cpp └── bench_LinkedHashMap.cpp ├── .gitignore ├── README.md ├── .semaphore └── semaphore.yml ├── LICENSE └── linked_hash.hpp /docs/result.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ez8-co/linked_hash/HEAD/docs/result.jpg -------------------------------------------------------------------------------- /docs/linked_hash_map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ez8-co/linked_hash/HEAD/docs/linked_hash_map.png -------------------------------------------------------------------------------- /docs/linked_hash_set.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ez8-co/linked_hash/HEAD/docs/linked_hash_set.png -------------------------------------------------------------------------------- /docs/linked_hash_map (LRU Mode).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ez8-co/linked_hash/HEAD/docs/linked_hash_map (LRU Mode).png -------------------------------------------------------------------------------- /docs/linked_hash_set (LRU Mode).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ez8-co/linked_hash/HEAD/docs/linked_hash_set (LRU Mode).png -------------------------------------------------------------------------------- /test/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "common.h" 3 | 4 | #ifdef _MSC_VER 5 | #pragma comment(lib, "gtest.lib") 6 | #endif 7 | 8 | vector* g_data; 9 | 10 | int main(int argc, char** argv) 11 | { 12 | testing::AddGlobalTestEnvironment (new bench_Environment); 13 | testing::InitGoogleTest(&argc, argv); 14 | return RUN_ALL_TESTS (); 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 2.8) 2 | CMAKE_POLICY(VERSION 2.8) 3 | 4 | PROJECT(linked_hash_st_bench) 5 | IF(CMAKE_SYSTEM_NAME MATCHES "Linux") 6 | LINK_LIBRARIES(gtest pthread) 7 | SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g2 -ggdb") 8 | SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall -std=c++11") 9 | ENDIF() 10 | AUX_SOURCE_DIRECTORY(. DIR_SRCS) 11 | 12 | ADD_EXECUTABLE(linked_hash_st_bench ${DIR_SRCS}) 13 | 14 | SET(CMAKE_BUILD_TYPE "Release") 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # linked_hash 2 | 3 | [![license](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat)](https://github.com/ez8-co/ezpp/blob/master/LICENSE) [![Build Status](https://orca-zhang.semaphoreci.com/badges/linked_hash/branches/master.svg?style=shields&key=252645d0-2585-4d99-99b7-aae2d1c44ec1)](https://orca-zhang.semaphoreci.com/projects/linked_hash) 4 | 5 | ## Test Results 6 | 7 | [Open Interactive Result Page](https://ez8-co.github.io/linked_hash/) 8 | 9 | ![](https://github.com/ez8-co/linked_hash/raw/master/docs/linked_hash_map.png) 10 | ![](https://github.com/ez8-co/linked_hash/raw/master/docs/linked_hash_map%20(LRU%20Mode).png) 11 | ![](https://github.com/ez8-co/linked_hash/raw/master/docs/linked_hash_set.png) 12 | ![](https://github.com/ez8-co/linked_hash/raw/master/docs/linked_hash_set%20(LRU%20Mode).png) 13 | -------------------------------------------------------------------------------- /.semaphore/semaphore.yml: -------------------------------------------------------------------------------- 1 | version: v1.0 2 | name: Initial Pipeline 3 | agent: 4 | machine: 5 | type: e1-standard-2 6 | os_image: ubuntu2004 7 | blocks: 8 | - name: 'Block #1' 9 | task: 10 | jobs: 11 | - name: 'Job #1' 12 | commands: 13 | - checkout 14 | - sudo apt-get -qq update 15 | - sudo apt-get install -y valgrind 16 | - 'sudo wget https://github.com/google/googletest/archive/release-1.7.0.tar.gz' 17 | - sudo tar xf release-1.7.0.tar.gz 18 | - cd googletest-release-1.7.0 19 | - sudo cmake -DBUILD_SHARED_LIBS=ON . 20 | - sudo make -j8 21 | - sudo cp -a include/gtest /usr/include 22 | - sudo cp -a libgtest_main.so libgtest.so /usr/lib/ 23 | - which valgrind 24 | - cd .. 25 | - ls -l 26 | - cd test 27 | - cmake . 28 | - make -j8 29 | - valgrind ./linked_hash_st_bench 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2010-2018 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. -------------------------------------------------------------------------------- /test/common.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "../linked_hash.hpp" 9 | 10 | #ifdef _MSC_VER 11 | #if _MSC_VER >= 1500 12 | #include 13 | #include 14 | #define unordered_map std::tr1::unordered_map 15 | #define unordered_set std::tr1::unordered_set 16 | #else 17 | #include 18 | #include 19 | #define unordered_map stdext::hash_map 20 | #define unordered_set stdext::hash_set 21 | #endif 22 | #else 23 | #ifdef _GXX_EXPERIMENTAL_CXX0X__ 24 | #include 25 | #include 26 | #define unordered_map std::tr1::unordered_map 27 | #define unordered_set std::tr1::unordered_set 28 | #elif __cplusplus >= 201103L 29 | #include 30 | #include 31 | #define unordered_map std::unordered_map 32 | #define unordered_set std::unordered_set 33 | #else 34 | #include 35 | #include 36 | #define unordered_map __gnu_cxx::hash_map 37 | #define unordered_set __gnu_cxx::hash_set 38 | #endif 39 | #endif 40 | 41 | using namespace std; 42 | #ifdef _MSC_VER 43 | using namespace stdext; 44 | #else 45 | using namespace __gnu_cxx; 46 | #endif 47 | 48 | #define DATA_COUNT 10000000 49 | #define DATA_RANGE 10000 50 | #define DATA_POP_CNT (DATA_RANGE / 2) 51 | 52 | template 53 | inline 54 | double 55 | BenchRun(const string& desc, FUNC_TYPE function, ARG_TYPE& arg, uint64_t times) 56 | { 57 | clock_t start; 58 | start = clock(); 59 | for(uint64_t i = 0; i < times; ++i) { 60 | function(arg, i); 61 | } 62 | double cost = (double)(clock() - start) / CLOCKS_PER_SEC; 63 | printf("[%-20s] Iters:%" PRId64 ", Cost time:%.2lf s\n", desc.c_str(), times, (double)cost); 64 | return cost; 65 | } 66 | 67 | template 68 | inline 69 | double 70 | BenchRun(const string& desc, FUNC_TYPE function, ARG_TYPE& arg, double* last = NULL) 71 | { 72 | clock_t start; 73 | start = clock(); 74 | function(arg); 75 | double cost = (double)(clock() - start) / CLOCKS_PER_SEC + (last ? *last : 0); 76 | printf("[%-20s] Cost time:%.2lf s\n", desc.c_str(),(double)cost); 77 | return cost; 78 | } 79 | 80 | template 81 | inline 82 | double 83 | NormalRun(FUNC_TYPE function, ARG_TYPE& arg, uint64_t times) 84 | { 85 | clock_t start; 86 | start = clock(); 87 | for(uint64_t i = 0; i < times; ++i) { 88 | function(arg, i); 89 | } 90 | return (double)(clock() - start) / CLOCKS_PER_SEC; 91 | } 92 | 93 | extern vector* g_data; 94 | 95 | class bench_Environment : public testing::Environment 96 | { 97 | public: 98 | virtual void SetUp() 99 | { 100 | srand((unsigned int)time(NULL)); 101 | g_data = new vector; 102 | g_data->reserve(DATA_COUNT); 103 | for(int64_t i = 0; i < DATA_COUNT; ++i) 104 | { 105 | g_data->push_back(rand() % DATA_RANGE); 106 | } 107 | printf("g_data size:%zd\n", g_data->size()); 108 | } 109 | virtual void TearDown() 110 | { 111 | g_data->clear(); 112 | delete g_data; 113 | } 114 | }; 115 | 116 | class st_find_test 117 | { 118 | public: 119 | template 120 | void operator()(T& container, uint64_t i) 121 | { 122 | ASSERT_TRUE(container.find((*g_data)[i]) != container.end()); 123 | } 124 | }; 125 | 126 | class st_count_test 127 | { 128 | public: 129 | template 130 | void operator()(T& container, uint64_t i) 131 | { 132 | ASSERT_TRUE(container.count((*g_data)[i]) == 1); 133 | } 134 | }; 135 | 136 | class st_for_each_test 137 | { 138 | public: 139 | template 140 | void operator()(T& container, uint64_t i) 141 | { 142 | for(typename T::const_iterator it = container.begin(); it != container.end(); ++it); 143 | } 144 | }; 145 | 146 | class st_erase_test 147 | { 148 | public: 149 | template 150 | void operator()(T& container, uint64_t i) 151 | { 152 | container.erase((*g_data)[i]); 153 | } 154 | }; 155 | 156 | class st_pop_front_test 157 | { 158 | public: 159 | template 160 | void operator()(linked_hash_set& container, uint64_t i) 161 | { 162 | container.pop_front(); 163 | } 164 | template 165 | void operator()(linked_hash_map& container, uint64_t i) 166 | { 167 | container.pop_front(); 168 | } 169 | template 170 | void operator()(T& container, uint64_t i) 171 | { 172 | container.erase(container.begin()); 173 | } 174 | }; 175 | 176 | class st_clear_test 177 | { 178 | public: 179 | template 180 | void operator()(T& container) 181 | { 182 | container.clear(); 183 | } 184 | }; 185 | 186 | class LRU_list_push_back_test 187 | { 188 | public: 189 | template 190 | void operator()(T& container, uint64_t i) 191 | { 192 | container.push_back((*g_data)[i]); 193 | } 194 | }; 195 | 196 | class LRU_pop_test 197 | { 198 | public: 199 | LRU_pop_test(int popCnt = DATA_POP_CNT) : cnt(popCnt) {} 200 | void operator()(list& cache) 201 | { 202 | 203 | while(cnt--) { 204 | cache.pop_front(); 205 | } 206 | } 207 | private: 208 | int cnt; 209 | }; 210 | 211 | template 212 | class LRU_dedup_test 213 | { 214 | public: 215 | void operator()(list& cache) 216 | { 217 | T tmp; 218 | for(list::reverse_iterator iter = cache.rbegin(); 219 | iter != cache.rend(); 220 | ) { 221 | typename T::iterator findIter = tmp.find(*iter); 222 | if(findIter == tmp.end()) { 223 | tmp.insert(*iter++); 224 | } 225 | else { 226 | iter = list::reverse_iterator(cache.erase((++iter).base())); 227 | } 228 | } 229 | LRU_pop_test()(cache); 230 | } 231 | }; 232 | 233 | class LRU_sort_unique_dedup_test 234 | { 235 | public: 236 | void operator()(list& cache) 237 | { 238 | cache.sort(); 239 | cache.unique(); 240 | LRU_pop_test()(cache); 241 | } 242 | }; 243 | 244 | class LRU_map_insert_test 245 | { 246 | public: 247 | template 248 | void operator()(T& container, uint64_t i) 249 | { 250 | typename T::iterator findIter = container.find((*g_data)[i]); 251 | if(findIter == container.end()) { 252 | container.insert(make_pair((*g_data)[i], clock())); 253 | } 254 | else { 255 | findIter->second = clock(); 256 | } 257 | } 258 | }; 259 | 260 | template 261 | class LRU_map_pop_test_aux 262 | { 263 | public: 264 | LRU_map_pop_test_aux(T& tmp) 265 | : _tmp(tmp) 266 | { 267 | } 268 | void operator()(K& cache) 269 | { 270 | for(typename K::iterator iter = cache.begin(); 271 | iter != cache.end(); 272 | ++iter) { 273 | _tmp[iter->second].insert(iter->first); 274 | } 275 | } 276 | private: 277 | T& _tmp; 278 | }; 279 | 280 | template 281 | class LRU_map_pop_test_sub 282 | { 283 | public: 284 | LRU_map_pop_test_sub(K& cache) 285 | : _cache(cache) 286 | { 287 | } 288 | void operator()(T& tmp) 289 | { 290 | int popCnt = DATA_POP_CNT; 291 | typename T::iterator iter = tmp.begin(); 292 | while(true) { 293 | set& vec = iter->second; 294 | for(set::iterator setIter = vec.begin(); 295 | setIter != vec.end(); 296 | ++setIter) { 297 | _cache.erase(*setIter); 298 | if(--popCnt == 0) { 299 | return; 300 | } 301 | } 302 | ++iter; 303 | } 304 | } 305 | private: 306 | K& _cache; 307 | }; 308 | 309 | template 310 | class LRU_map_pop_test 311 | { 312 | public: 313 | void operator()(K& cache) 314 | { 315 | BenchRun (" 1.build aux", LRU_map_pop_test_aux (_tmp), cache); 316 | BenchRun (" 2.cache pop", LRU_map_pop_test_sub (cache), _tmp); 317 | } 318 | 319 | private: 320 | T _tmp; 321 | }; 322 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | 16 | 225 | 226 | 227 | -------------------------------------------------------------------------------- /test/bench_LinkedHashSet.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "common.h" 3 | 4 | class bench_LinkedHashSet : public testing::Test 5 | { 6 | protected: 7 | bench_LinkedHashSet(void) 8 | { 9 | } 10 | 11 | virtual ~bench_LinkedHashSet(void) 12 | { 13 | } 14 | 15 | static void SetUpTestCase() 16 | { 17 | } 18 | 19 | static void TearDownTestCase() 20 | { 21 | } 22 | 23 | virtual void SetUp(void) 24 | { 25 | } 26 | 27 | virtual void TearDown(void) 28 | { 29 | } 30 | }; 31 | 32 | class set_insert_test 33 | { 34 | public: 35 | template 36 | void operator()(T& container, uint64_t i) 37 | { 38 | container.insert((*g_data)[i]); 39 | } 40 | }; 41 | 42 | TEST_F(bench_LinkedHashSet, for_each) 43 | { 44 | set testSet; 45 | NormalRun(set_insert_test(), testSet, DATA_COUNT); 46 | BenchRun("set", st_for_each_test(), testSet, testSet.size()); 47 | 48 | unordered_set testHashSet; 49 | NormalRun(set_insert_test(), testHashSet, DATA_COUNT); 50 | BenchRun("unordered_set", st_for_each_test(), testHashSet, testHashSet.size()); 51 | 52 | linked_hash_set testLinkedHashSet; 53 | NormalRun(set_insert_test(), testLinkedHashSet, DATA_COUNT); 54 | BenchRun("linked_hash_set", st_for_each_test(), testLinkedHashSet, testLinkedHashSet.size()); 55 | } 56 | 57 | TEST_F(bench_LinkedHashSet, insert) 58 | { 59 | set testSet; 60 | BenchRun("set", set_insert_test(), testSet, DATA_COUNT); 61 | 62 | unordered_set testHashSet; 63 | BenchRun("unordered_set", set_insert_test(), testHashSet, DATA_COUNT); 64 | ASSERT_EQ(testSet.size(), testHashSet.size()); 65 | 66 | linked_hash_set testLinkedHashSet; 67 | BenchRun("linked_hash_set", set_insert_test(), testLinkedHashSet, DATA_COUNT); 68 | ASSERT_EQ(testSet.size(), testLinkedHashSet.size()); 69 | } 70 | 71 | TEST_F(bench_LinkedHashSet, count) 72 | { 73 | set testSet; 74 | NormalRun(set_insert_test(), testSet, DATA_COUNT); 75 | BenchRun("set", st_count_test(), testSet, DATA_COUNT); 76 | 77 | unordered_set testHashSet; 78 | NormalRun(set_insert_test(), testHashSet, DATA_COUNT); 79 | BenchRun("unordered_set", st_count_test(), testHashSet, DATA_COUNT); 80 | 81 | linked_hash_set testLinkedHashSet; 82 | NormalRun(set_insert_test(), testLinkedHashSet, DATA_COUNT); 83 | BenchRun("linked_hash_set", st_count_test(), testLinkedHashSet, DATA_COUNT); 84 | } 85 | 86 | TEST_F(bench_LinkedHashSet, find) 87 | { 88 | set testSet; 89 | NormalRun(set_insert_test(), testSet, DATA_COUNT); 90 | BenchRun("set", st_find_test(), testSet, DATA_COUNT); 91 | 92 | unordered_set testHashSet; 93 | NormalRun(set_insert_test(), testHashSet, DATA_COUNT); 94 | BenchRun("unordered_set", st_find_test(), testHashSet, DATA_COUNT); 95 | 96 | linked_hash_set testLinkedHashSet; 97 | NormalRun(set_insert_test(), testLinkedHashSet, DATA_COUNT); 98 | BenchRun("linked_hash_set", st_find_test(), testLinkedHashSet, DATA_COUNT); 99 | } 100 | 101 | TEST_F(bench_LinkedHashSet, erase) 102 | { 103 | set testSet; 104 | NormalRun(set_insert_test(), testSet, DATA_COUNT); 105 | BenchRun("set", st_erase_test(), testSet, DATA_COUNT); 106 | 107 | unordered_set testHashSet; 108 | NormalRun(set_insert_test(), testHashSet, DATA_COUNT); 109 | BenchRun("unordered_set", st_erase_test(), testHashSet, DATA_COUNT); 110 | 111 | linked_hash_set testLinkedHashSet; 112 | NormalRun(set_insert_test(), testLinkedHashSet, DATA_COUNT); 113 | BenchRun("linked_hash_set", st_erase_test(), testLinkedHashSet, DATA_COUNT); 114 | } 115 | 116 | TEST_F(bench_LinkedHashSet, pop_front) 117 | { 118 | set testSet; 119 | NormalRun(set_insert_test(), testSet, DATA_COUNT); 120 | BenchRun("set", st_pop_front_test(), testSet, testSet.size()); 121 | ASSERT_TRUE(testSet.empty()); 122 | 123 | unordered_set testHashSet; 124 | NormalRun(set_insert_test(), testHashSet, DATA_COUNT); 125 | BenchRun("unordered_set", st_pop_front_test(), testHashSet, testHashSet.size()); 126 | ASSERT_TRUE(testHashSet.empty()); 127 | 128 | linked_hash_set testLinkedHashSet; 129 | NormalRun(set_insert_test(), testLinkedHashSet, DATA_COUNT); 130 | BenchRun("linked_hash_set", st_pop_front_test(), testLinkedHashSet, testLinkedHashSet.size()); 131 | ASSERT_TRUE(testLinkedHashSet.empty()); 132 | } 133 | 134 | TEST_F(bench_LinkedHashSet, clear) 135 | { 136 | set testSet; 137 | NormalRun(set_insert_test(), testSet, DATA_COUNT); 138 | BenchRun("set", st_clear_test(), testSet); 139 | ASSERT_TRUE(testSet.empty()); 140 | 141 | unordered_set testHashSet; 142 | NormalRun(set_insert_test(), testHashSet, DATA_COUNT); 143 | BenchRun("unordered_set", st_clear_test(), testHashSet); 144 | ASSERT_TRUE(testHashSet.empty()); 145 | 146 | linked_hash_set testLinkedHashSet; 147 | NormalRun(set_insert_test(), testLinkedHashSet, DATA_COUNT); 148 | BenchRun("linked_hash_set", st_clear_test(), testLinkedHashSet); 149 | ASSERT_TRUE(testLinkedHashSet.empty()); 150 | } 151 | 152 | class LRU_linked_hash_set_insert_test 153 | { 154 | public: 155 | template 156 | void operator()(T& container, uint64_t i) 157 | { 158 | typename T::iterator findIter = container.access((*g_data)[i]); 159 | if(findIter == container.end()) { 160 | container.insert((*g_data)[i]); 161 | } 162 | } 163 | }; 164 | 165 | class LRU_linked_hash_set_pop_test 166 | { 167 | public: 168 | template 169 | void operator()(T& container) 170 | { 171 | int popCnt = DATA_POP_CNT; 172 | while(popCnt--) { 173 | container.pop_front(); 174 | } 175 | } 176 | }; 177 | 178 | template 179 | bool CompareCache_Set(D& d, S& s) 180 | { 181 | if(d.size() != s. size()) { 182 | return false; 183 | } 184 | typename D::iterator Diter = d.begin(); 185 | typename S::iterator Siter = s.begin(); 186 | for(; 187 | Diter != d.end(); 188 | ++Diter, ++Siter) { 189 | if((*Diter) !=(*Siter)) { 190 | return false; 191 | } 192 | } 193 | return true; 194 | } 195 | 196 | template 197 | bool ReverseCompareCache_Set(D& d, S& s) 198 | { 199 | if(d.size() != s. size()) { 200 | return false; 201 | } 202 | typename D::reverse_iterator DReverseIter = d.rbegin(); 203 | typename S::reverse_iterator SReverseIter = s.rbegin(); 204 | for(; 205 | DReverseIter != d.rend(); 206 | ++DReverseIter, ++SReverseIter) { 207 | if((*DReverseIter) !=(*SReverseIter)) { 208 | return false; 209 | } 210 | } 211 | return true; 212 | } 213 | 214 | template 215 | bool PopFrontCheck_Set(D& d, S& s) 216 | { 217 | if(d.size() != s. size()) { 218 | return false; 219 | } 220 | for(typename D::iterator Diter = d.begin(); 221 | Diter != d.end(); 222 | ++Diter) { 223 | if((*Diter) != s.front()) { 224 | return false; 225 | } 226 | s.pop_front(); 227 | } 228 | return true; 229 | } 230 | 231 | TEST_F(bench_LinkedHashSet, LRUModeTest) 232 | { 233 | printf("######### list cache with dedup ##########\n"); 234 | list testListWithSet; 235 | double cost = BenchRun("==ESTABLISH==", LRU_list_push_back_test(), testListWithSet, DATA_COUNT); 236 | BenchRun("set dedup", LRU_dedup_test< set >(), testListWithSet, &cost); 237 | 238 | list testListWithHashSet; 239 | NormalRun(LRU_list_push_back_test(), testListWithHashSet, DATA_COUNT); 240 | BenchRun("unordered_set dedup", LRU_dedup_test< unordered_set >(), testListWithHashSet, &cost); 241 | ASSERT_EQ(testListWithSet.size(), testListWithHashSet.size()); 242 | 243 | list testListSortUnique; 244 | NormalRun(LRU_list_push_back_test(), testListSortUnique, DATA_COUNT); 245 | BenchRun("sort+unique dedup", LRU_sort_unique_dedup_test(), testListSortUnique, &cost); 246 | ASSERT_EQ(testListWithSet.size(), testListSortUnique.size()); 247 | 248 | printf("\n######### map cache with timestamp aux ##########\n"); 249 | map testMapWithTimeAuxMap; 250 | cost = BenchRun("==ESTABLISH==", LRU_map_insert_test(), testMapWithTimeAuxMap, DATA_COUNT); 251 | BenchRun("map", LRU_map_pop_test< map >, map >(), testMapWithTimeAuxMap, &cost); 252 | ASSERT_EQ(testMapWithTimeAuxMap.size(), DATA_RANGE - DATA_POP_CNT); 253 | 254 | printf("\n######### unordered_map cache with timestamp aux ##########\n"); 255 | unordered_map testHashMapWithTimeAuxMap; 256 | cost = BenchRun("==ESTABLISH==", LRU_map_insert_test(), testHashMapWithTimeAuxMap, DATA_COUNT); 257 | BenchRun("unordered_map", LRU_map_pop_test< map >, unordered_map >(), testHashMapWithTimeAuxMap, &cost); 258 | ASSERT_EQ(testHashMapWithTimeAuxMap.size(), DATA_RANGE - DATA_POP_CNT); 259 | 260 | printf("\n######### linked hash set cache ##########\n"); 261 | linked_hash_set testLinkedHashSet; 262 | cost = BenchRun("==ESTABLISH==", LRU_linked_hash_set_insert_test(), testLinkedHashSet, DATA_COUNT); 263 | BenchRun("cache pop", LRU_linked_hash_set_pop_test(), testLinkedHashSet, &cost); 264 | ASSERT_EQ(testLinkedHashSet.size(), DATA_RANGE - DATA_POP_CNT); 265 | ASSERT_TRUE(CompareCache_Set(testListWithSet, testLinkedHashSet)); 266 | ASSERT_TRUE(ReverseCompareCache_Set(testListWithSet, testLinkedHashSet)); 267 | ASSERT_TRUE(PopFrontCheck_Set(testListWithSet, testLinkedHashSet)); 268 | 269 | printf("\n######### simple list pop benchmark ##########\n"); 270 | list testList; 271 | cost = BenchRun("==ESTABLISH==", LRU_list_push_back_test(), testList, DATA_COUNT); 272 | BenchRun("cache pop", LRU_pop_test(DATA_COUNT - testListWithSet.size()), testList, &cost); 273 | ASSERT_EQ(testList.size(), DATA_RANGE - DATA_POP_CNT); 274 | } 275 | -------------------------------------------------------------------------------- /test/bench_LinkedHashMap.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "common.h" 3 | 4 | class bench_LinkedHashMap : public testing::Test 5 | { 6 | protected: 7 | bench_LinkedHashMap(void) 8 | { 9 | } 10 | 11 | virtual ~bench_LinkedHashMap(void) 12 | { 13 | } 14 | 15 | static void SetUpTestCase() 16 | { 17 | } 18 | 19 | static void TearDownTestCase() 20 | { 21 | } 22 | 23 | virtual void SetUp(void) 24 | { 25 | } 26 | 27 | virtual void TearDown(void) 28 | { 29 | } 30 | }; 31 | 32 | class map_insert_test 33 | { 34 | public: 35 | template 36 | void operator()(T& container, uint64_t i) 37 | { 38 | container.insert(make_pair((*g_data)[i],(*g_data)[i])); 39 | } 40 | }; 41 | 42 | TEST_F(bench_LinkedHashMap, for_each) 43 | { 44 | map testMap; 45 | NormalRun(map_insert_test(), testMap, DATA_COUNT); 46 | BenchRun("map", st_for_each_test(), testMap, testMap.size()); 47 | 48 | unordered_map testHashMap; 49 | NormalRun(map_insert_test(), testHashMap, DATA_COUNT); 50 | BenchRun("unordered_map", st_for_each_test(), testHashMap, testHashMap.size()); 51 | 52 | linked_hash_map testLinkedHashMap; 53 | NormalRun(map_insert_test(), testLinkedHashMap, DATA_COUNT); 54 | BenchRun("linked_hash_map", st_for_each_test(), testLinkedHashMap, testLinkedHashMap.size()); 55 | } 56 | 57 | TEST_F(bench_LinkedHashMap, insert) 58 | { 59 | map testMap; 60 | BenchRun("map", map_insert_test(), testMap, DATA_COUNT); 61 | 62 | unordered_map testHashMap; 63 | BenchRun("unordered_map", map_insert_test(), testHashMap, DATA_COUNT); 64 | ASSERT_EQ(testMap.size(), testHashMap.size()); 65 | 66 | linked_hash_map testLinkedHashMap; 67 | BenchRun("linked_hash_map", map_insert_test(), testLinkedHashMap, DATA_COUNT); 68 | ASSERT_EQ(testMap.size(), testLinkedHashMap.size()); 69 | } 70 | 71 | TEST_F(bench_LinkedHashMap, count) 72 | { 73 | map testMap; 74 | NormalRun(map_insert_test(), testMap, DATA_COUNT); 75 | BenchRun("map", st_count_test(), testMap, DATA_COUNT); 76 | 77 | unordered_map testHashMap; 78 | NormalRun(map_insert_test(), testHashMap, DATA_COUNT); 79 | BenchRun("unordered_map", st_count_test(), testHashMap, DATA_COUNT); 80 | 81 | linked_hash_map testLinkedHashMap; 82 | NormalRun(map_insert_test(), testLinkedHashMap, DATA_COUNT); 83 | BenchRun("linked_hash_map", st_count_test(), testLinkedHashMap, DATA_COUNT); 84 | } 85 | 86 | TEST_F(bench_LinkedHashMap, find) 87 | { 88 | map testMap; 89 | NormalRun(map_insert_test(), testMap, DATA_COUNT); 90 | BenchRun("map", st_find_test(), testMap, DATA_COUNT); 91 | 92 | unordered_map testHashMap; 93 | NormalRun(map_insert_test(), testHashMap, DATA_COUNT); 94 | BenchRun("unordered_map", st_find_test(), testHashMap, DATA_COUNT); 95 | 96 | linked_hash_map testLinkedHashMap; 97 | NormalRun(map_insert_test(), testLinkedHashMap, DATA_COUNT); 98 | BenchRun("linked_hash_map", st_find_test(), testLinkedHashMap, DATA_COUNT); 99 | } 100 | 101 | TEST_F(bench_LinkedHashMap, erase) 102 | { 103 | map testMap; 104 | NormalRun(map_insert_test(), testMap, DATA_COUNT); 105 | BenchRun("map", st_erase_test(), testMap, DATA_COUNT); 106 | 107 | unordered_map testHashMap; 108 | NormalRun(map_insert_test(), testHashMap, DATA_COUNT); 109 | BenchRun("unordered_map", st_erase_test(), testHashMap, DATA_COUNT); 110 | 111 | linked_hash_map testLinkedHashMap; 112 | NormalRun(map_insert_test(), testLinkedHashMap, DATA_COUNT); 113 | BenchRun("linked_hash_map", st_erase_test(), testLinkedHashMap, DATA_COUNT); 114 | } 115 | 116 | TEST_F(bench_LinkedHashMap, pop_front) 117 | { 118 | map testMap; 119 | NormalRun(map_insert_test(), testMap, DATA_COUNT); 120 | BenchRun("map", st_pop_front_test(), testMap, testMap.size()); 121 | ASSERT_TRUE(testMap.empty()); 122 | 123 | unordered_map testHashMap; 124 | NormalRun(map_insert_test(), testHashMap, DATA_COUNT); 125 | BenchRun("unordered_map", st_pop_front_test(), testHashMap, testHashMap.size()); 126 | ASSERT_TRUE(testHashMap.empty()); 127 | 128 | linked_hash_map testLinkedHashMap; 129 | NormalRun(map_insert_test(), testLinkedHashMap, DATA_COUNT); 130 | BenchRun("linked_hash_map", st_pop_front_test(), testLinkedHashMap, testLinkedHashMap.size()); 131 | ASSERT_TRUE(testLinkedHashMap.empty()); 132 | } 133 | 134 | TEST_F(bench_LinkedHashMap, clear) 135 | { 136 | map testMap; 137 | NormalRun(map_insert_test(), testMap, DATA_COUNT); 138 | BenchRun("map", st_clear_test(), testMap); 139 | ASSERT_TRUE(testMap.empty()); 140 | 141 | unordered_map testHashMap; 142 | NormalRun(map_insert_test(), testHashMap, DATA_COUNT); 143 | BenchRun("unordered_map", st_clear_test(), testHashMap); 144 | ASSERT_TRUE(testHashMap.empty()); 145 | 146 | linked_hash_map testLinkedHashMap; 147 | NormalRun(map_insert_test(), testLinkedHashMap, DATA_COUNT); 148 | BenchRun("linked_hash_map", st_clear_test(), testLinkedHashMap); 149 | ASSERT_TRUE(testLinkedHashMap.empty()); 150 | } 151 | 152 | class LRU_linked_hash_map_insert_test 153 | { 154 | public: 155 | template 156 | void operator()(T& container, uint64_t i) 157 | { 158 | typename T::iterator findIter = container.access((*g_data)[i]); 159 | if(findIter == container.end()) { 160 | container.insert(make_pair((*g_data)[i],(*g_data)[i])); 161 | } 162 | } 163 | }; 164 | 165 | class LRU_linked_hash_map_pop_test 166 | { 167 | public: 168 | template 169 | void operator()(T& container) 170 | { 171 | int popCnt = DATA_POP_CNT; 172 | while(popCnt--) { 173 | container.pop_front(); 174 | } 175 | } 176 | }; 177 | 178 | template 179 | bool CompareCache_Map(D& d, S& s) 180 | { 181 | if(d.size() != s. size()) { 182 | return false; 183 | } 184 | typename D::iterator Diter = d.begin(); 185 | typename S::iterator Siter = s.begin(); 186 | for(; 187 | Diter != d.end() && Siter != s.end(); 188 | ++Diter, ++Siter) { 189 | if((*Diter) != Siter->first) { 190 | return false; 191 | } 192 | } 193 | return true; 194 | } 195 | 196 | template 197 | bool ReverseCompareCache_Map(D& d, S& s) 198 | { 199 | if(d.size() != s. size()) { 200 | return false; 201 | } 202 | typename D::reverse_iterator DReverseIter = d.rbegin(); 203 | typename S::reverse_iterator SReverseIter = s.rbegin(); 204 | for(; 205 | DReverseIter != d.rend() && SReverseIter != s.rend(); 206 | ++DReverseIter, ++SReverseIter) { 207 | if((*DReverseIter) != SReverseIter->first) { 208 | return false; 209 | } 210 | } 211 | return true; 212 | } 213 | 214 | template 215 | bool PopFrontCheck_Map(D& d, S& s) 216 | { 217 | if(d.size() != s. size()) { 218 | return false; 219 | } 220 | for(typename D::iterator Diter = d.begin(); 221 | Diter != d.end(); 222 | ++Diter) { 223 | if((*Diter) != s.front()) { 224 | return false; 225 | } 226 | s.pop_front(); 227 | } 228 | return true; 229 | } 230 | 231 | TEST_F(bench_LinkedHashMap, LRUModeTest) 232 | { 233 | printf("######### list cache with dedup ##########\n"); 234 | list testListWithSet; 235 | double cost = BenchRun("==ESTABLISH==", LRU_list_push_back_test(), testListWithSet, DATA_COUNT); 236 | BenchRun("set dedup", LRU_dedup_test< set >(), testListWithSet, &cost); 237 | 238 | list testListWithHashSet; 239 | NormalRun(LRU_list_push_back_test(), testListWithHashSet, DATA_COUNT); 240 | BenchRun("unordered_set dedup", LRU_dedup_test< unordered_set >(), testListWithHashSet, &cost); 241 | ASSERT_EQ(testListWithSet.size(), testListWithHashSet.size()); 242 | 243 | list testListSortUnique; 244 | NormalRun(LRU_list_push_back_test(), testListSortUnique, DATA_COUNT); 245 | BenchRun("sort+unique dedup", LRU_sort_unique_dedup_test(), testListSortUnique, &cost); 246 | ASSERT_EQ(testListWithSet.size(), testListSortUnique.size()); 247 | 248 | printf("\n######### map cache with timestamp aux ##########\n"); 249 | map testMapWithTimeAuxMap; 250 | cost = BenchRun("==ESTABLISH==", LRU_map_insert_test(), testMapWithTimeAuxMap, DATA_COUNT); 251 | BenchRun("map", LRU_map_pop_test< map >, map >(), testMapWithTimeAuxMap, &cost); 252 | ASSERT_EQ(testMapWithTimeAuxMap.size(), DATA_RANGE - DATA_POP_CNT); 253 | 254 | printf("\n######### unordered_map cache with timestamp aux ##########\n"); 255 | unordered_map testHashMapWithTimeAuxMap; 256 | cost = BenchRun("==ESTABLISH==", LRU_map_insert_test(), testHashMapWithTimeAuxMap, DATA_COUNT); 257 | BenchRun("unordered_map", LRU_map_pop_test< map >, unordered_map >(), testHashMapWithTimeAuxMap, &cost); 258 | ASSERT_EQ(testHashMapWithTimeAuxMap.size(), DATA_RANGE - DATA_POP_CNT); 259 | 260 | printf("\n######### linked hash map cache ##########\n"); 261 | linked_hash_map testLinkedHashMap; 262 | cost = BenchRun("==ESTABLISH==", LRU_linked_hash_map_insert_test(), testLinkedHashMap, DATA_COUNT); 263 | BenchRun("cache pop", LRU_linked_hash_map_pop_test(), testLinkedHashMap, &cost); 264 | ASSERT_EQ(testLinkedHashMap.size(), DATA_RANGE - DATA_POP_CNT); 265 | ASSERT_TRUE(CompareCache_Map(testListWithSet, testLinkedHashMap)); 266 | ASSERT_TRUE(ReverseCompareCache_Map(testListWithSet, testLinkedHashMap)); 267 | ASSERT_TRUE(PopFrontCheck_Map(testListWithSet, testLinkedHashMap)); 268 | 269 | printf("\n######### simple list pop benchmark ##########\n"); 270 | list testList; 271 | cost = BenchRun("==ESTABLISH==", LRU_list_push_back_test(), testList, DATA_COUNT); 272 | BenchRun("cache pop", LRU_pop_test(DATA_COUNT - testListWithSet.size()), testList, &cost); 273 | ASSERT_EQ(testList.size(), DATA_RANGE - DATA_POP_CNT); 274 | } 275 | -------------------------------------------------------------------------------- /linked_hash.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #ifdef _MSC_VER 7 | #if _MSC_VER >= 1500 8 | #include 9 | #define lh_hash_set std::tr1::unordered_set 10 | #define lh_hash_fcn std::tr1::hash 11 | #else 12 | #include 13 | #define lh_hash_set stdext::hash_set 14 | #define lh_hash_fcn stdext::hash_compare 15 | #endif 16 | #else 17 | #ifdef _GXX_EXPERIMENTAL_CXX0X__ 18 | #include 19 | #define lh_hash_set std::tr1::unordered_set 20 | #define lh_hash_fcn std::tr1::hash 21 | #elif __cplusplus >= 201103L 22 | #include 23 | #define lh_hash_set std::unordered_set 24 | #define lh_hash_fcn std::hash 25 | #else 26 | #include 27 | #define lh_hash_set __gnu_cxx::hash_set 28 | #define lh_hash_fcn __gnu_cxx::hash 29 | #endif 30 | #endif 31 | 32 | 33 | template > 34 | class linked_hash_set; 35 | 36 | template > 37 | class linked_hash_map; 38 | 39 | 40 | template 41 | struct lh_entry 42 | { 43 | lh_entry(lh_entry* p = NULL, lh_entry* n = NULL) : val() , prev(p), next(n) {} 44 | lh_entry(const value_type& v, lh_entry* p, lh_entry* n) : val(v), prev(p), next(n) {} 45 | 46 | value_type val; 47 | lh_entry *prev, *next; 48 | }; 49 | 50 | template 51 | struct lh_const_iter : public std::iterator 52 | { 53 | typedef _Ty value_type; 54 | typedef value_type& reference; 55 | typedef value_type* pointer; 56 | typedef const value_type& const_reference; 57 | typedef const value_type* const_pointer; 58 | 59 | explicit lh_const_iter(lh_entry* ptr) : _ptr(ptr) {} 60 | const_reference operator*() const { return static_cast(_ptr->val); } 61 | const_pointer operator->() const { return (&**this); } 62 | lh_const_iter& operator++() { _ptr = _ptr->next; return (*this); } 63 | lh_const_iter operator++(int) { lh_const_iter tmp = *this; ++*this; return (tmp); } 64 | lh_const_iter& operator--() { _ptr = _ptr->prev; return (*this); } 65 | lh_const_iter operator--(int) { lh_const_iter tmp = *this; --*this; return (tmp); } 66 | 67 | bool operator==(const lh_const_iter& rhs) const { return (_ptr == rhs._ptr); } 68 | bool operator!=(const lh_const_iter& rhs) const { return (!(*this == rhs)); } 69 | 70 | protected: 71 | lh_entry* _ptr; 72 | }; 73 | 74 | template 75 | struct lh_iter : public lh_const_iter<_Ty> 76 | { 77 | typedef _Ty value_type; 78 | typedef value_type& reference; 79 | typedef value_type* pointer; 80 | 81 | explicit lh_iter(lh_entry*const ptr) : lh_const_iter(ptr) {} 82 | reference operator*() const { return ((reference)**(lh_const_iter*)this); } 83 | pointer operator->() const { return (&**this); } 84 | lh_iter& operator++() { ++(*(lh_const_iter*)this); return (*this); } 85 | lh_iter& operator--() { --(*(lh_const_iter*)this); return (*this); } 86 | }; 87 | 88 | #if !defined(_MSC_VER) && !defined(_GXX_EXPERIMENTAL_CXX0X__) && __cplusplus < 201103L 89 | #include 90 | namespace __gnu_cxx 91 | { 92 | template<> 93 | struct hash 94 | { 95 | inline size_t operator()(const std::string& s) const { 96 | return __stl_hash_string(s.c_str()); 97 | } 98 | }; 99 | } 100 | #endif 101 | 102 | namespace std 103 | { 104 | template 105 | struct equal_to*> 106 | { 107 | inline bool operator()(const lh_entry<_Kty>* lhs, const lh_entry<_Kty>* rhs) const { 108 | return cmp(lhs->val, rhs->val); 109 | } 110 | 111 | private: 112 | equal_to<_Kty> cmp; 113 | }; 114 | 115 | template 116 | struct equal_to >*> 117 | { 118 | inline bool operator()(const lh_entry >* lhs, const lh_entry >* rhs) const { 119 | return cmp(lhs->val.first, rhs->val.first); 120 | } 121 | 122 | private: 123 | equal_to<_Kty> cmp; 124 | }; 125 | } 126 | 127 | template */> 128 | class linked_hash_set 129 | { 130 | private: 131 | struct lhs_hasher 132 | { 133 | lhs_hasher() : cmp() {} 134 | #if defined(_MSC_VER) && _MSC_VER < 1500 135 | enum { // parameters for hash table 136 | bucket_size = 4, // 0 < bucket_size 137 | min_buckets = 8 138 | }; // min_buckets = 2 ^^ N, 0 < N 139 | 140 | inline bool operator()(const lh_entry<_Kty>* lhs, const lh_entry<_Kty>* rhs) const { 141 | return cmp(lhs->val, rhs->val); 142 | } 143 | #else 144 | inline size_t operator()(const lh_entry<_Kty>* entry) const { 145 | return cmp(entry->val); 146 | } 147 | #endif 148 | 149 | private: 150 | HashFcn cmp; 151 | }; // end class lhs_hasher 152 | 153 | public: 154 | typedef lh_hash_set*, lhs_hasher> _linked_hash_set; 155 | typedef typename _linked_hash_set::iterator _lhs_iter; 156 | typedef typename _linked_hash_set::const_iterator _lhs_const_iter; 157 | typedef typename _linked_hash_set::size_type size_type; 158 | typedef _Kty key_type; 159 | typedef _Kty value_type; 160 | typedef value_type* pointer; 161 | typedef value_type& reference; 162 | typedef const value_type* const_pointer; 163 | typedef const value_type& const_reference; 164 | typedef lh_iter iterator; 165 | typedef lh_const_iter const_iterator; 166 | typedef std::reverse_iterator reverse_iterator; 167 | typedef std::reverse_iterator const_reverse_iterator; 168 | typedef std::pair _Pairib; 169 | typedef std::pair _Pairii; 170 | typedef std::pair _Paircc; 171 | 172 | linked_hash_set() : _head(&_head, &_head) {} 173 | linked_hash_set(const linked_hash_set& rhs) : _head(&_head, &_head) { assign(rhs); } 174 | linked_hash_set& operator=(const linked_hash_set& rhs) { if(this != &rhs) { clear(); assign(rhs); } return *this; } 175 | ~linked_hash_set() { clear(); } 176 | 177 | iterator find(const key_type& key) { 178 | _lhs_iter it = _lhs.find((lh_entry*)&key); 179 | return iterator(it != _lhs.end() ? *it : &_head); 180 | } 181 | 182 | const_iterator find(const key_type& key) const { 183 | _lhs_const_iter it = _lhs.find((lh_entry*)&key); 184 | return const_iterator(it != _lhs.end() ? *it : &_head); 185 | } 186 | 187 | size_type count(const key_type& key) const { return _lhs.count((lh_entry*)&key); } 188 | 189 | iterator access(const key_type& key); 190 | 191 | _Pairib insert(const key_type& value); 192 | 193 | size_type size() const { return _lhs.size(); } 194 | size_type max_size() const { return _lhs.max_size(); } 195 | bool empty() const { return _lhs.empty(); } 196 | 197 | iterator begin() { return iterator(_head.next); } 198 | const_iterator begin() const { return const_iterator(_head.next); } 199 | iterator end() { return iterator(&_head); } 200 | const_iterator end() const { return const_iterator(&_head); } 201 | reverse_iterator rbegin() { return reverse_iterator(end()); } 202 | const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } 203 | reverse_iterator rend() { return reverse_iterator(begin()); } 204 | const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } 205 | 206 | const _Kty& front() { assert(!empty()); return _head.next->val; } 207 | const _Kty& front() const { assert(!empty()); return _head.next->val; } 208 | void pop_front(); 209 | 210 | size_type erase(const key_type& key); 211 | void erase(const_iterator iter) { erase(*iter); } 212 | void erase(const_iterator first, const_iterator last); 213 | void clear(); 214 | 215 | private: 216 | void assign(const linked_hash_set& rhs); 217 | 218 | lh_entry _head; 219 | _linked_hash_set _lhs; 220 | }; // end class linked_hash_set 221 | 222 | template 223 | void 224 | linked_hash_set<_Kty, HashFcn>::assign(const linked_hash_set& rhs) 225 | { 226 | if(this != &rhs) { 227 | lh_entry<_Kty>* pos = rhs._head.next; 228 | while(pos != &rhs._head) { 229 | lh_entry<_Kty>* entry = new lh_entry<_Kty>(pos->val, _head.prev, &_head); 230 | _lhs.insert(entry); 231 | _head.prev->next = entry; 232 | _head.prev = entry; 233 | pos = pos->next; 234 | } 235 | } 236 | } 237 | 238 | template 239 | typename linked_hash_set<_Kty, HashFcn>::_Pairib 240 | linked_hash_set<_Kty, HashFcn>::insert(const key_type& value) 241 | { 242 | if(!_lhs.count((lh_entry*)&value)) { 243 | lh_entry* entry = new lh_entry(value, _head.prev, &_head); 244 | std::pair<_lhs_iter, bool> ib = _lhs.insert(entry); 245 | if(ib.second) { 246 | _head.prev->next = entry; 247 | _head.prev = entry; 248 | return std::make_pair(iterator(*ib.first), true); 249 | } 250 | delete entry; 251 | } 252 | return std::make_pair(iterator(&_head), false); 253 | } 254 | 255 | template 256 | typename linked_hash_set<_Kty, HashFcn>::iterator 257 | linked_hash_set<_Kty, HashFcn>::access(const key_type& key) 258 | { 259 | _lhs_iter it = _lhs.find((lh_entry*)&key); 260 | if(it != _lhs.end()) { 261 | // remove it from the link 262 | (*it)->prev->next =(*it)->next; 263 | (*it)->next->prev =(*it)->prev; 264 | 265 | // relink to tail 266 | (*it)->prev = _head.prev; 267 | (*it)->next = &_head; 268 | _head.prev->next = *it; 269 | _head.prev = *it; 270 | return iterator(*it); 271 | } 272 | return iterator(&_head); 273 | } 274 | 275 | template 276 | void 277 | linked_hash_set<_Kty, HashFcn>::pop_front() 278 | { 279 | assert(!empty()); 280 | lh_entry* entry = _head.next; 281 | _lhs.erase(entry); 282 | _head.next = entry->next; 283 | _head.next->prev = &_head; 284 | delete entry; 285 | } 286 | 287 | template 288 | typename linked_hash_set<_Kty, HashFcn>::size_type 289 | linked_hash_set<_Kty, HashFcn>::erase(const key_type& key) 290 | { 291 | _lhs_iter it = _lhs.find((lh_entry*)&key); 292 | if(it != _lhs.end()) { 293 | // remove it from the link 294 | (*it)->prev->next =(*it)->next; 295 | (*it)->next->prev =(*it)->prev; 296 | lh_entry<_Kty>* entry =(*it); 297 | _lhs.erase((*it)); 298 | delete entry; 299 | return 1; 300 | } 301 | return 0; 302 | } 303 | 304 | template 305 | void 306 | linked_hash_set<_Kty, HashFcn>::erase(const_iterator first, const_iterator last) 307 | { 308 | if(first == begin() && last == end()) 309 | clear(); 310 | else { 311 | const_iterator it = first; 312 | while(it != last) { 313 | erase(it++); 314 | } 315 | } 316 | } 317 | 318 | template 319 | void 320 | linked_hash_set<_Kty, HashFcn>::clear() 321 | { 322 | lh_entry* pos = _head.next; 323 | _lhs.clear(); 324 | _head.prev = _head.next = &_head; 325 | while(pos != &_head) { 326 | lh_entry* next = pos->next; 327 | delete pos; 328 | pos = next; 329 | } 330 | } 331 | 332 | template */> 333 | class linked_hash_map 334 | { 335 | private: 336 | struct lhm_hasher 337 | { 338 | typedef std::pair<_Kty, _Ty> value_type; 339 | lhm_hasher() : cmp() {} 340 | #if defined(_MSC_VER) && _MSC_VER < 1500 341 | enum { // parameters for hash table 342 | bucket_size = 4, // 0 < bucket_size 343 | min_buckets = 8 344 | }; // min_buckets = 2 ^^ N, 0 < N 345 | 346 | inline bool operator()(const lh_entry* lhs, const lh_entry* rhs) const { 347 | return cmp(lhs->val.first, rhs->val.first); 348 | } 349 | #else 350 | inline size_t operator()(const lh_entry* entry) const { 351 | return cmp(entry->val.first); 352 | } 353 | #endif 354 | 355 | private: 356 | HashFcn cmp; 357 | }; // end class lhm_hasher 358 | 359 | public: 360 | typedef _Kty key_type; 361 | typedef std::pair<_Kty, _Ty> value_type; 362 | typedef lh_hash_set*, lhm_hasher> _linked_hash_map; 363 | typedef typename _linked_hash_map::iterator _lhm_iter; 364 | typedef typename _linked_hash_map::const_iterator _lhm_const_iter; 365 | typedef typename _linked_hash_map::size_type size_type; 366 | typedef value_type* pointer; 367 | typedef value_type& reference; 368 | typedef const value_type* const_pointer; 369 | typedef const value_type& const_reference; 370 | typedef lh_iter iterator; 371 | typedef lh_const_iter const_iterator; 372 | typedef std::reverse_iterator reverse_iterator; 373 | typedef std::reverse_iterator const_reverse_iterator; 374 | typedef std::pair _Pairib; 375 | typedef std::pair _Pairii; 376 | typedef std::pair _Paircc; 377 | 378 | linked_hash_map() : _head(&_head, &_head) {} 379 | linked_hash_map(const linked_hash_map& rhs) : _head(&_head, &_head) { assign(rhs); } 380 | linked_hash_map& operator=(const linked_hash_map& rhs) { if(this != &rhs) { clear(); assign(rhs); } return *this; } 381 | ~linked_hash_map() { clear(); } 382 | 383 | iterator find(const key_type& key) { 384 | _lhm_iter it = _lhm.find((lh_entry*)&key); 385 | return iterator(it != _lhm.end() ? *it : &_head); 386 | } 387 | 388 | const_iterator find(const key_type& key) const { 389 | _lhm_const_iter it = _lhm.find((lh_entry*)&key); 390 | return const_iterator(it != _lhm.end() ? *it : &_head); 391 | } 392 | 393 | size_type count(const key_type& key) const { return _lhm.count((lh_entry*)&key); } 394 | 395 | iterator access(const key_type& key); 396 | 397 | _Pairib insert(const value_type& value); 398 | 399 | size_type size() const { return (_lhm.size()); } 400 | size_type max_size() const { return (_lhm.max_size()); } 401 | bool empty() const { return (_lhm.empty()); } 402 | 403 | iterator begin() { return iterator(_head.next); } 404 | const_iterator begin() const { return const_iterator(_head.next); } 405 | iterator end() { return iterator(&_head); } 406 | const_iterator end() const { return const_iterator(&_head); } 407 | reverse_iterator rbegin() { return (reverse_iterator(end())); } 408 | const_reverse_iterator rbegin() const { return (const_reverse_iterator(end())); } 409 | reverse_iterator rend() { return (reverse_iterator(begin())); } 410 | const_reverse_iterator rend() const { return (const_reverse_iterator(begin())); } 411 | 412 | _Ty& operator [](const key_type& key); 413 | 414 | _Ty& front() { assert(!empty()); return _head.next->val.second; } 415 | const _Ty& front() const { assert(!empty()); return _head.next->val.second;} 416 | void pop_front(); 417 | 418 | size_type erase(const key_type& key); 419 | void erase(const_iterator iter) { erase(iter->first); } 420 | void erase(const_iterator first, const_iterator last); 421 | 422 | void clear(); 423 | 424 | private: 425 | void assign(const linked_hash_map& rhs); 426 | 427 | lh_entry _head; 428 | _linked_hash_map _lhm; 429 | }; // end class linked_hash_map 430 | 431 | template 432 | void 433 | linked_hash_map<_Kty, _Ty, HashFcn>::assign(const linked_hash_map& rhs) 434 | { 435 | if(this != &rhs) { 436 | lh_entry* pos = rhs._head.next; 437 | while(pos != &rhs._head) { 438 | lh_entry* entry = new lh_entry(pos->val, _head.prev, &_head); 439 | _lhm.insert(entry); 440 | _head.prev->next = entry; 441 | _head.prev = entry; 442 | pos = pos->next; 443 | } 444 | } 445 | } 446 | 447 | template 448 | typename linked_hash_map<_Kty, _Ty, HashFcn>::_Pairib 449 | linked_hash_map<_Kty, _Ty, HashFcn>::insert(const value_type& value) 450 | { 451 | if(!_lhm.count((lh_entry*)&value)) { 452 | lh_entry* entry = new lh_entry(value, _head.prev, &_head); 453 | std::pair<_lhm_iter, bool> ib = _lhm.insert(entry); 454 | if(ib.second) { 455 | _head.prev->next = entry; 456 | _head.prev = entry; 457 | return std::make_pair(iterator(*ib.first), true); 458 | } 459 | delete entry; 460 | } 461 | return std::make_pair(iterator(&_head), false); 462 | } 463 | 464 | template 465 | _Ty& 466 | linked_hash_map<_Kty, _Ty, HashFcn>::operator[](const key_type& key) 467 | { 468 | _lhm_iter it = _lhm.find((lh_entry*)&key); 469 | if(it == _lhm.end()) { 470 | lh_entry* entry = new lh_entry(std::make_pair(key, _Ty()), _head.prev, &_head); 471 | _lhm.insert(entry); 472 | _head.prev->next = entry; 473 | _head.prev = entry; 474 | return entry->val.second; 475 | } 476 | return (*it)->val.second; 477 | } 478 | 479 | template 480 | typename linked_hash_map<_Kty, _Ty, HashFcn>::iterator 481 | linked_hash_map<_Kty, _Ty, HashFcn>::access(const key_type& key) 482 | { 483 | _lhm_iter it = _lhm.find((lh_entry*)&key); 484 | if(it != _lhm.end()) { 485 | // remove it from the link 486 | (*it)->prev->next =(*it)->next; 487 | (*it)->next->prev =(*it)->prev; 488 | 489 | // relink to tail 490 | (*it)->prev = _head.prev; 491 | (*it)->next = &_head; 492 | _head.prev->next = *it; 493 | _head.prev = *it; 494 | return iterator(*it); 495 | } 496 | return iterator(&_head); 497 | } 498 | 499 | template 500 | void 501 | linked_hash_map<_Kty, _Ty, HashFcn>::pop_front() 502 | { 503 | assert(!empty()); 504 | lh_entry* entry = _head.next; 505 | _lhm.erase(entry); 506 | _head.next = entry->next; 507 | _head.next->prev = &_head; 508 | delete entry; 509 | } 510 | 511 | template 512 | typename linked_hash_map<_Kty, _Ty, HashFcn>::size_type 513 | linked_hash_map<_Kty, _Ty, HashFcn>::erase(const key_type& key) 514 | { 515 | _lhm_iter it = _lhm.find((lh_entry*)&key); 516 | if(it != _lhm.end()) { 517 | // remove it from the link 518 | (*it)->prev->next =(*it)->next; 519 | (*it)->next->prev =(*it)->prev; 520 | lh_entry* entry = *it; 521 | _lhm.erase(entry); 522 | delete entry; 523 | return 1; 524 | } 525 | return 0; 526 | } 527 | 528 | template 529 | void 530 | linked_hash_map<_Kty, _Ty, HashFcn>::erase(const_iterator first, const_iterator last) 531 | { 532 | if(first == begin() && last == end()) 533 | clear(); 534 | else { 535 | const_iterator it = first; 536 | while(it != last) { 537 | erase(it++); 538 | } 539 | } 540 | } 541 | 542 | template 543 | void 544 | linked_hash_map<_Kty, _Ty, HashFcn>::clear() 545 | { 546 | lh_entry* pos = _head.next; 547 | _lhm.clear(); 548 | _head.prev = _head.next = &_head; 549 | while(pos != &_head) { 550 | lh_entry* next = pos->next; 551 | delete pos; 552 | pos = next; 553 | } 554 | } 555 | --------------------------------------------------------------------------------