3434#include < iostream>
3535#include < mutex>
3636#include < optional>
37+ #include < shared_mutex>
3738#include < stdexcept>
3839#include < vector>
3940
4041namespace LouiEriksson {
4142
4243 /* *
43- * @mainpage Version 2.2.1
44+ * @mainpage Version 2.3.0
4445 * @details Custom Hashmap implementation accepting a customisable key and value type.
4546 * This implementation requires that your "key" type is compatible with std::hash and that the stored data types are copyable.
4647 * @see Wang, Q. (Harry) (2020). Implementing Your Own HashMap (Explanation + Code). YouTube.
@@ -51,7 +52,7 @@ namespace LouiEriksson {
5152 template <typename Tk, typename Tv>
5253 class Hashmap final {
5354
54- inline static std::recursive_mutex s_Lock;
55+ inline static std::shared_mutex s_Lock;
5556
5657 public:
5758
@@ -130,12 +131,35 @@ namespace LouiEriksson {
130131 for (auto & bucket : shallow_cpy) {
131132 for (auto & kvp : bucket) {
132133
133- std::exception_ptr e_ptr = nullptr ;
134+ auto & k = kvp.first ;
135+ auto & v = kvp.second ;
134136
135- Assign (std::move (kvp.first ), std::move (kvp.second ), e_ptr);
137+ if (m_Size >= m_Buckets.size ()) {
138+ Resize (m_Buckets.size () * 2U );
139+ }
140+
141+ // Create an index by taking the key's hash value and "wrapping" it with the number of buckets.
142+ const auto hash = GetHashcode (k);
143+ const auto i = hash % m_Buckets.size ();
144+
145+ auto & bucket = m_Buckets[i];
146+
147+ auto exists = false ;
148+ for (auto & kvp : bucket) {
149+
150+ if (GetHashcode (kvp.first ) == hash) {
151+ exists = true ;
152+
153+ kvp.second = std::move (v);
154+
155+ break ;
156+ }
157+ }
158+
159+ if (!exists) {
160+ m_Size++;
136161
137- if (e_ptr != nullptr ) {
138- std::rethrow_exception (e_ptr);
162+ bucket.emplace_back (std::move (k), std::move (v));
139163 }
140164 }
141165 }
@@ -156,8 +180,6 @@ namespace LouiEriksson {
156180 */
157181 const Tv& Return (const Tk& _key) const {
158182
159- const std::lock_guard<std::recursive_mutex> lock (s_Lock);
160-
161183 const Tv* result = nullptr ;
162184
163185 if (!m_Buckets.empty ()) {
@@ -247,7 +269,7 @@ namespace LouiEriksson {
247269 * @return The number of items stored within the Hashmap.
248270 */
249271 [[nodiscard]] const size_t & size () const noexcept {
250- const std::lock_guard<std::recursive_mutex> lock (s_Lock);
272+ const std::shared_lock lock (s_Lock);
251273
252274 return m_Size;
253275 }
@@ -268,7 +290,7 @@ namespace LouiEriksson {
268290 */
269291 bool ContainsKey (const Tk& _key) const noexcept {
270292
271- const std::lock_guard<std::recursive_mutex> lock (s_Lock);
293+ const std::shared_lock lock (s_Lock);
272294
273295 auto result = false ;
274296
@@ -303,8 +325,8 @@ namespace LouiEriksson {
303325 */
304326 bool ContainsKey (const Tk& _key, std::exception_ptr& _exception) const noexcept {
305327
306- const std::lock_guard<std::recursive_mutex> lock (s_Lock);
307-
328+ const std::shared_lock lock (s_Lock);
329+
308330 auto result = false ;
309331
310332 try {
@@ -341,7 +363,7 @@ namespace LouiEriksson {
341363 */
342364 bool Add (const Tk& _key, const Tv& _value) noexcept {
343365
344- const std::lock_guard<std::recursive_mutex> lock (s_Lock);
366+ const std::unique_lock lock (s_Lock);
345367
346368 auto result = true ;
347369
@@ -390,7 +412,7 @@ namespace LouiEriksson {
390412 */
391413 bool Add (const Tk& _key, const Tv& _value, std::exception_ptr& _exception) noexcept {
392414
393- const std::lock_guard<std::recursive_mutex> lock (s_Lock);
415+ const std::unique_lock lock (s_Lock);
394416
395417 auto result = true ;
396418
@@ -440,7 +462,7 @@ namespace LouiEriksson {
440462 */
441463 bool Add (const Tk&& _key, const Tv&& _value) noexcept {
442464
443- const std::lock_guard<std::recursive_mutex> lock (s_Lock);
465+ const std::unique_lock lock (s_Lock);
444466
445467 auto result = true ;
446468
@@ -489,7 +511,7 @@ namespace LouiEriksson {
489511 */
490512 bool Add (const Tk&& _key, const Tv&& _value, std::exception_ptr& _exception) noexcept {
491513
492- const std::lock_guard<std::recursive_mutex> lock (s_Lock);
514+ const std::unique_lock lock (s_Lock);
493515
494516 auto result = true ;
495517
@@ -537,7 +559,7 @@ namespace LouiEriksson {
537559 */
538560 void Assign (const Tk& _key, const Tv& _value) noexcept {
539561
540- const std::lock_guard<std::recursive_mutex> lock (s_Lock);
562+ const std::unique_lock lock (s_Lock);
541563
542564 try {
543565
@@ -581,7 +603,7 @@ namespace LouiEriksson {
581603 */
582604 void Assign (const Tk& _key, const Tv& _value, std::exception_ptr& _exception) noexcept {
583605
584- const std::lock_guard<std::recursive_mutex> lock (s_Lock);
606+ const std::unique_lock lock (s_Lock);
585607
586608 try {
587609
@@ -626,7 +648,7 @@ namespace LouiEriksson {
626648 */
627649 void Assign (Tk&& _key, Tv&& _value) noexcept {
628650
629- const std::lock_guard<std::recursive_mutex> lock (s_Lock);
651+ const std::unique_lock lock (s_Lock);
630652
631653 try {
632654
@@ -670,7 +692,7 @@ namespace LouiEriksson {
670692 */
671693 void Assign (Tk&& _key, Tv&& _value, std::exception_ptr& _exception) noexcept {
672694
673- const std::lock_guard<std::recursive_mutex> lock (s_Lock);
695+ const std::unique_lock lock (s_Lock);
674696
675697 try {
676698
@@ -715,7 +737,7 @@ namespace LouiEriksson {
715737 */
716738 bool Remove (const Tk& _key) noexcept {
717739
718- const std::lock_guard<std::recursive_mutex> lock (s_Lock);
740+ const std::unique_lock lock (s_Lock);
719741
720742 bool result = false ;
721743
@@ -756,7 +778,7 @@ namespace LouiEriksson {
756778 */
757779 bool Remove (const Tk& _key, std::exception_ptr& _exception) noexcept {
758780
759- const std::lock_guard<std::recursive_mutex> lock (s_Lock);
781+ const std::unique_lock lock (s_Lock);
760782
761783 bool result = false ;
762784
@@ -800,7 +822,7 @@ namespace LouiEriksson {
800822 */
801823 optional_ref Get (const Tk& _key) const noexcept {
802824
803- const std::lock_guard<std::recursive_mutex> lock (s_Lock);
825+ const std::shared_lock lock (s_Lock);
804826
805827 typename optional_ref::optional_t result = std::nullopt ;
806828
@@ -839,7 +861,7 @@ namespace LouiEriksson {
839861 */
840862 optional_ref Get (const Tk& _key, std::exception_ptr& _exception) const noexcept {
841863
842- const std::lock_guard<std::recursive_mutex> lock (s_Lock);
864+ const std::shared_lock lock (s_Lock);
843865
844866 typename optional_ref::optional_t result = std::nullopt ;
845867
@@ -874,7 +896,7 @@ namespace LouiEriksson {
874896 */
875897 void Trim () {
876898
877- const std::lock_guard<std::recursive_mutex> lock (s_Lock);
899+ const std::unique_lock lock (s_Lock);
878900
879901 size_t trimStart = 1U ;
880902
@@ -895,7 +917,7 @@ namespace LouiEriksson {
895917 */
896918 [[nodiscard]] std::vector<Tk> Keys () const {
897919
898- const std::lock_guard<std::recursive_mutex> lock (s_Lock);
920+ const std::shared_lock lock (s_Lock);
899921
900922 std::vector<Tk> result;
901923
@@ -914,7 +936,7 @@ namespace LouiEriksson {
914936 */
915937 [[nodiscard]] std::vector<Tv> Values () const {
916938
917- const std::lock_guard<std::recursive_mutex> lock (s_Lock);
939+ const std::shared_lock lock (s_Lock);
918940
919941 std::vector<Tv> result;
920942
@@ -933,7 +955,7 @@ namespace LouiEriksson {
933955 */
934956 [[nodiscard]] std::vector<KeyValuePair> GetAll () const {
935957
936- const std::lock_guard<std::recursive_mutex> lock (s_Lock);
958+ const std::shared_lock lock (s_Lock);
937959
938960 std::vector<KeyValuePair> result;
939961
@@ -953,7 +975,7 @@ namespace LouiEriksson {
953975 */
954976 void Reserve (const std::size_t & _newSize) {
955977
956- const std::lock_guard<std::recursive_mutex> lock (s_Lock);
978+ const std::unique_lock lock (s_Lock);
957979
958980 if (m_Size < _newSize) {
959981 Resize (_newSize);
@@ -965,7 +987,7 @@ namespace LouiEriksson {
965987 */
966988 void Clear () noexcept {
967989
968- const std::lock_guard<std::recursive_mutex> lock (s_Lock);
990+ const std::unique_lock lock (s_Lock);
969991
970992 try {
971993 m_Buckets.clear ();
@@ -991,6 +1013,9 @@ namespace LouiEriksson {
9911013 [[deprecated(" This function does not guarantee exception-safety and will explicitly throw if no entry exists. Consider using Get() if exception-safe access is required.\n Suppress this warning by defining \" HASHMAP_SUPPRESS_UNSAFE_WARNING\" ." )]]
9921014#endif
9931015 const Tv& operator [](const Tk& _key) const {
1016+
1017+ const std::shared_lock lock (s_Lock);
1018+
9941019 return Return (_key);
9951020 }
9961021
0 commit comments