diff --git a/include/etl/unordered_map.h b/include/etl/unordered_map.h index 7dabff95..c74fce4a 100644 --- a/include/etl/unordered_map.h +++ b/include/etl/unordered_map.h @@ -663,6 +663,8 @@ namespace etl pbucket->insert_after(pbucket->before_begin(), node); + adjust_first_last_markers_after_insert(pbucket); + return pbucket->begin()->key_value_pair.second; } @@ -791,10 +793,10 @@ namespace etl // Just add the pointer to the bucket; bucket.insert_after(bucket.before_begin(), node); + adjust_first_last_markers_after_insert(pbucket); + result.first = iterator((pbuckets + number_of_buckets), pbucket, pbucket->begin()); result.second = true; - - adjust_first_last_markers(pbucket); } else { @@ -824,6 +826,7 @@ namespace etl // Add the node to the end of the bucket; bucket.insert_after(inode_previous, node); + adjust_first_last_markers_after_insert(&bucket); ++inode_previous; result.first = iterator((pbuckets + number_of_buckets), pbucket, inode_previous); @@ -889,6 +892,7 @@ namespace etl bucket.erase_after(iprevious); // Unlink from the bucket. icurrent->key_value_pair.~value_type(); // Destroy the value. pnodepool->release(&*icurrent); // Release it back to the pool. + adjust_first_last_markers_after_erase(&bucket); n = 1; ETL_DECREMENT_DEBUG_COUNT } @@ -919,6 +923,7 @@ namespace etl bucket.erase_after(iprevious); // Unlink from the bucket. icurrent->key_value_pair.~value_type(); // Destroy the value. pnodepool->release(&*icurrent); // Release it back to the pool. + adjust_first_last_markers_after_erase(&bucket); ETL_DECREMENT_DEBUG_COUNT return inext; @@ -954,6 +959,7 @@ namespace etl local_iterator inext = pbucket->erase_after(iprevious); // Unlink from the bucket. icurrent->key_value_pair.~value_type(); // Destroy the value. pnodepool->release(&*icurrent); // Release it back to the pool. + adjust_first_last_markers_after_erase(pbucket); ETL_DECREMENT_DEBUG_COUNT icurrent = inext; @@ -1245,15 +1251,68 @@ namespace etl //********************************************************************* /// Adjust the first and last markers according to the new entry. //********************************************************************* - void adjust_first_last_markers(bucket_t* pbucket) + void adjust_first_last_markers_after_insert(bucket_t* pbucket) { - if (pbucket < first) + if (size() == 1) { first = pbucket; + last = pbucket; } - else if (pbucket > last) + else { - last = pbucket; + if (pbucket < first) + { + first = pbucket; + } + else if (pbucket > last) + { + last = pbucket; + } + } + } + + //********************************************************************* + /// Adjust the first and last markers according to the erased entry. + //********************************************************************* + void adjust_first_last_markers_after_erase(bucket_t* pbucket) + { + if (empty()) + { + first = pbuckets; + last = pbuckets; + } + else + { + if (pbucket == first) + { + // We erased the first so, we need to search again from where we erased. + while (first->empty()) + { + ++first; + } + } + else if (pbucket == last) + { + // We erased the last, so we need to search again. Start from the first, go no further than the current last. + bucket_t* pbucket = first; + bucket_t* pend = last; + + last = first; + + while (pbucket != pend) + { + if (!pbucket->empty()) + { + last = pbucket; + } + + ++pbucket; + } + } + else + { + // Nothing to do. + } } } diff --git a/include/etl/version.h b/include/etl/version.h index 0fc8a57d..35a86903 100644 --- a/include/etl/version.h +++ b/include/etl/version.h @@ -39,7 +39,7 @@ SOFTWARE. #define ETL_VERSION_MAJOR 14 #define ETL_VERSION_MINOR 10 -#define ETL_VERSION_PATCH 0 +#define ETL_VERSION_PATCH 1 #define ETL_VERSION ETL_STRINGIFY(ETL_VERSION_MAJOR) ETL_STRINGIFY(ETL_VERSION_MINOR) ETL_STRINGIFY(ETL_VERSION_PATCH) #define ETL_VERSION_W ETL_WIDE_STRING(ETL_CONCAT(ETL_CONCAT(ETL_VERSION_MAJOR, ETL_VERSION_MINOR), ETL_VERSION_PATCH)) diff --git a/support/Release notes.txt b/support/Release notes.txt index 2aed565d..3dfa22ad 100644 --- a/support/Release notes.txt +++ b/support/Release notes.txt @@ -1,3 +1,7 @@ +=============================================================================== +14.10.1 +Fix for unordered_map insert and erase bug. + =============================================================================== 14.10.0 Added ETL_NOEXCEPT macros. diff --git a/test/test_unordered_map.cpp b/test/test_unordered_map.cpp index eff6662d..fb03c947 100644 --- a/test/test_unordered_map.cpp +++ b/test/test_unordered_map.cpp @@ -28,6 +28,8 @@ SOFTWARE. #include "UnitTest++.h" +#include + #include #include #include @@ -687,9 +689,35 @@ namespace } //************************************************************************* - TEST_FIXTURE(SetupFixture, test_release) + TEST_FIXTURE(SetupFixture, test_insert_and_erase_bug) { + etl::unordered_map map; + map[1] = 'b'; + map[2] = 'c'; + map[3] = 'd'; + map[4] = 'e'; + + auto it = map.find(1); + map.erase(it); + + it = map.find(4); + map.erase(it); + + std::vector s; + + for (const auto &kv : map) + { + std::stringstream ss; + ss << "map[" << kv.first << "] = " << kv.second; + s.push_back(ss.str()); + } + + CHECK_EQUAL(2, s.size()); + CHECK_EQUAL("map[2] = c", s[0]); + CHECK_EQUAL("map[3] = d", s[1]); + CHECK_EQUAL('c', map[2]); + CHECK_EQUAL('d', map[3]); } }; }