LCOV - code coverage report
Current view: top level - src/immer/detail/hamts - champ.hpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 116 152 76.3 %
Date: 2025-02-23 09:33:43 Functions: 25 25 100.0 %

          Line data    Source code
       1             : //
       2             : // immer: immutable data structures for C++
       3             : // Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
       4             : //
       5             : // This software is distributed under the Boost Software License, Version 1.0.
       6             : // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
       7             : //
       8             : 
       9             : #pragma once
      10             : 
      11             : #include <immer/config.hpp>
      12             : #include <immer/detail/hamts/node.hpp>
      13             : 
      14             : #include <algorithm>
      15             : 
      16             : namespace immer {
      17             : namespace detail {
      18             : namespace hamts {
      19             : 
      20             : template <typename T,
      21             :           typename Hash,
      22             :           typename Equal,
      23             :           typename MemoryPolicy,
      24             :           bits_t B>
      25             : struct champ
      26             : {
      27             :     static constexpr auto bits = B;
      28             : 
      29             :     using node_t   = node<T, Hash, Equal, MemoryPolicy, B>;
      30             :     using bitmap_t = typename get_bitmap_type<B>::type;
      31             : 
      32             :     static_assert(branches<B> <= sizeof(bitmap_t) * 8, "");
      33             : 
      34             :     node_t* root;
      35             :     size_t size;
      36             : 
      37     1548205 :     static node_t* empty()
      38             :     {
      39     1548205 :         static const auto node = node_t::make_inner_n(0);
      40     1548205 :         return node->inc();
      41             :     }
      42             : 
      43           6 :     champ(node_t* r, size_t sz = 0)
      44             :         : root{r}
      45      415727 :         , size{sz}
      46         447 :     {}
      47             : 
      48      271962 :     champ(const champ& other)
      49      271962 :         : champ{other.root, other.size}
      50             :     {
      51      271962 :         inc();
      52           0 :     }
      53             : 
      54       29011 :     champ(champ&& other)
      55       29011 :         : champ{empty()}
      56             :     {
      57       29011 :         swap(*this, other);
      58             :     }
      59             : 
      60      271962 :     champ& operator=(const champ& other)
      61             :     {
      62      543924 :         auto next = other;
      63      271962 :         swap(*this, next);
      64      543924 :         return *this;
      65             :     }
      66             : 
      67             :     champ& operator=(champ&& other)
      68             :     {
      69       56455 :         swap(*this, other);
      70             :         return *this;
      71             :     }
      72             : 
      73      357428 :     friend void swap(champ& x, champ& y)
      74             :     {
      75             :         using std::swap;
      76      357428 :         swap(x.root, y.root);
      77       56449 :         swap(x.size, y.size);
      78             :     }
      79             : 
      80      329990 :     ~champ() { dec(); }
      81             : 
      82      271962 :     void inc() const { root->inc(); }
      83             : 
      84     2150126 :     void dec() const
      85             :     {
      86     2150126 :         if (root->dec())
      87       28972 :             node_t::delete_deep(root, 0);
      88     2150126 :     }
      89             : 
      90             :     template <typename Fn>
      91             :     void for_each_chunk(Fn&& fn) const
      92             :     {
      93             :         for_each_chunk_traversal(root, 0, fn);
      94             :     }
      95             : 
      96             :     template <typename Fn>
      97             :     void for_each_chunk_traversal(node_t* node, count_t depth, Fn&& fn) const
      98             :     {
      99             :         if (depth < max_depth<B>) {
     100             :             auto datamap = node->datamap();
     101             :             if (datamap)
     102             :                 fn(node->values(), node->values() + node->data_count());
     103             :             auto nodemap = node->nodemap();
     104             :             if (nodemap) {
     105             :                 auto fst = node->children();
     106             :                 auto lst = fst + node->children_count();
     107             :                 for (; fst != lst; ++fst)
     108             :                     for_each_chunk_traversal(*fst, depth + 1, fn);
     109             :             }
     110             :         } else {
     111             :             fn(node->collisions(),
     112             :                node->collisions() + node->collision_count());
     113             :         }
     114             :     }
     115             : 
     116             :     template <typename Project, typename Default, typename K>
     117      766921 :     decltype(auto) get(const K& k) const
     118             :     {
     119      766921 :         auto node = root;
     120      766921 :         auto hash = Hash{}(k);
     121      831627 :         for (auto i = count_t{}; i < max_depth<B>; ++i) {
     122      831627 :             auto bit = bitmap_t{1u} << (hash & mask<B>);
     123      831627 :             if (node->nodemap() & bit) {
     124       64706 :                 auto offset = node->children_count(bit);
     125       64706 :                 node        = node->children()[offset];
     126       64706 :                 hash        = hash >> B;
     127      766921 :             } else if (node->datamap() & bit) {
     128      227828 :                 auto offset = node->data_count(bit);
     129      227828 :                 auto val    = node->values() + offset;
     130      227828 :                 if (Equal{}(*val, k))
     131      226217 :                     return Project{}(*val);
     132             :                 else
     133             :                     return Default{}();
     134             :             } else {
     135             :                 return Default{}();
     136             :             }
     137             :         }
     138           0 :         auto fst = node->collisions();
     139           0 :         auto lst = fst + node->collision_count();
     140           0 :         for (; fst != lst; ++fst)
     141           0 :             if (Equal{}(*fst, k))
     142       29629 :                 return Project{}(*fst);
     143             :         return Default{}();
     144             :     }
     145             : 
     146             :     std::pair<node_t*, bool>
     147       33700 :     do_add(node_t* node, T v, hash_t hash, shift_t shift) const
     148             :     {
     149       33700 :         assert(node);
     150       33700 :         if (shift == max_shift<B>) {
     151           0 :             auto fst = node->collisions();
     152           0 :             auto lst = fst + node->collision_count();
     153           0 :             for (; fst != lst; ++fst)
     154           0 :                 if (Equal{}(*fst, v))
     155             :                     return {
     156           0 :                         node_t::copy_collision_replace(node, fst, std::move(v)),
     157           0 :                         false};
     158           0 :             return {node_t::copy_collision_insert(node, std::move(v)), true};
     159             :         } else {
     160       33700 :             auto idx = (hash & (mask<B> << shift)) >> shift;
     161       33700 :             auto bit = bitmap_t{1u} << idx;
     162       33700 :             if (node->nodemap() & bit) {
     163        5136 :                 auto offset = node->children_count(bit);
     164        5136 :                 assert(node->children()[offset]);
     165        5136 :                 auto result = do_add(
     166        5136 :                     node->children()[offset], std::move(v), hash, shift + B);
     167             :                 IMMER_TRY {
     168        5136 :                     result.first =
     169        5136 :                         node_t::copy_inner_replace(node, offset, result.first);
     170        5136 :                     return result;
     171             :                 }
     172           0 :                 IMMER_CATCH (...) {
     173           0 :                     node_t::delete_deep_shift(result.first, shift + B);
     174           0 :                     IMMER_RETHROW;
     175             :                 }
     176       28564 :             } else if (node->datamap() & bit) {
     177       23310 :                 auto offset = node->data_count(bit);
     178       23310 :                 auto val    = node->values() + offset;
     179       23310 :                 if (Equal{}(*val, v))
     180       21974 :                     return {node_t::copy_inner_replace_value(
     181       21974 :                                 node, offset, std::move(v)),
     182       21974 :                             false};
     183             :                 else {
     184        1336 :                     auto child = node_t::make_merged(
     185        1336 :                         shift + B, std::move(v), hash, *val, Hash{}(*val));
     186             :                     IMMER_TRY {
     187        1336 :                         return {node_t::copy_inner_replace_merged(
     188             :                                     node, bit, offset, child),
     189        1336 :                                 true};
     190             :                     }
     191           0 :                     IMMER_CATCH (...) {
     192           0 :                         node_t::delete_deep_shift(child, shift + B);
     193           0 :                         IMMER_RETHROW;
     194             :                     }
     195             :                 }
     196             :             } else {
     197             :                 return {
     198        5254 :                     node_t::copy_inner_insert_value(node, bit, std::move(v)),
     199        5254 :                     true};
     200             :             }
     201             :         }
     202             :     }
     203             : 
     204       28564 :     champ add(T v) const
     205             :     {
     206       28564 :         auto hash     = Hash{}(v);
     207       28564 :         auto res      = do_add(root, std::move(v), hash, 0);
     208       28564 :         auto new_size = size + (res.second ? 1 : 0);
     209       28564 :         return {res.first, new_size};
     210             :     }
     211             : 
     212             :     template <typename Project,
     213             :               typename Default,
     214             :               typename Combine,
     215             :               typename K,
     216             :               typename Fn>
     217             :     std::pair<node_t*, bool>
     218             :     do_update(node_t* node, K&& k, Fn&& fn, hash_t hash, shift_t shift) const
     219             :     {
     220             :         if (shift == max_shift<B>) {
     221             :             auto fst = node->collisions();
     222             :             auto lst = fst + node->collision_count();
     223             :             for (; fst != lst; ++fst)
     224             :                 if (Equal{}(*fst, k))
     225             :                     return {
     226             :                         node_t::copy_collision_replace(
     227             :                             node,
     228             :                             fst,
     229             :                             Combine{}(std::forward<K>(k),
     230             :                                       std::forward<Fn>(fn)(Project{}(*fst)))),
     231             :                         false};
     232             :             return {node_t::copy_collision_insert(
     233             :                         node,
     234             :                         Combine{}(std::forward<K>(k),
     235             :                                   std::forward<Fn>(fn)(Default{}()))),
     236             :                     true};
     237             :         } else {
     238             :             auto idx = (hash & (mask<B> << shift)) >> shift;
     239             :             auto bit = bitmap_t{1u} << idx;
     240             :             if (node->nodemap() & bit) {
     241             :                 auto offset = node->children_count(bit);
     242             :                 auto result = do_update<Project, Default, Combine>(
     243             :                     node->children()[offset],
     244             :                     k,
     245             :                     std::forward<Fn>(fn),
     246             :                     hash,
     247             :                     shift + B);
     248             :                 IMMER_TRY {
     249             :                     result.first =
     250             :                         node_t::copy_inner_replace(node, offset, result.first);
     251             :                     return result;
     252             :                 }
     253             :                 IMMER_CATCH (...) {
     254             :                     node_t::delete_deep_shift(result.first, shift + B);
     255             :                     IMMER_RETHROW;
     256             :                 }
     257             :             } else if (node->datamap() & bit) {
     258             :                 auto offset = node->data_count(bit);
     259             :                 auto val    = node->values() + offset;
     260             :                 if (Equal{}(*val, k))
     261             :                     return {
     262             :                         node_t::copy_inner_replace_value(
     263             :                             node,
     264             :                             offset,
     265             :                             Combine{}(std::forward<K>(k),
     266             :                                       std::forward<Fn>(fn)(Project{}(*val)))),
     267             :                         false};
     268             :                 else {
     269             :                     auto child = node_t::make_merged(
     270             :                         shift + B,
     271             :                         Combine{}(std::forward<K>(k),
     272             :                                   std::forward<Fn>(fn)(Default{}())),
     273             :                         hash,
     274             :                         *val,
     275             :                         Hash{}(*val));
     276             :                     IMMER_TRY {
     277             :                         return {node_t::copy_inner_replace_merged(
     278             :                                     node, bit, offset, child),
     279             :                                 true};
     280             :                     }
     281             :                     IMMER_CATCH (...) {
     282             :                         node_t::delete_deep_shift(child, shift + B);
     283             :                         IMMER_RETHROW;
     284             :                     }
     285             :                 }
     286             :             } else {
     287             :                 return {node_t::copy_inner_insert_value(
     288             :                             node,
     289             :                             bit,
     290             :                             Combine{}(std::forward<K>(k),
     291             :                                       std::forward<Fn>(fn)(Default{}()))),
     292             :                         true};
     293             :             }
     294             :         }
     295             :     }
     296             : 
     297             :     template <typename Project,
     298             :               typename Default,
     299             :               typename Combine,
     300             :               typename K,
     301             :               typename Fn>
     302             :     champ update(const K& k, Fn&& fn) const
     303             :     {
     304             :         auto hash = Hash{}(k);
     305             :         auto res  = do_update<Project, Default, Combine>(
     306             :             root, k, std::forward<Fn>(fn), hash, 0);
     307             :         auto new_size = size + (res.second ? 1 : 0);
     308             :         return {res.first, new_size};
     309             :     }
     310             : 
     311             :     // basically:
     312             :     //      variant<monostate_t, T*, node_t*>
     313             :     // boo bad we are not using... C++17 :'(
     314             :     struct sub_result
     315             :     {
     316             :         enum kind_t
     317             :         {
     318             :             nothing,
     319             :             singleton,
     320             :             tree
     321             :         };
     322             : 
     323             :         union data_t
     324             :         {
     325             :             T* singleton;
     326             :             node_t* tree;
     327             :         };
     328             : 
     329             :         kind_t kind;
     330             :         data_t data;
     331             : 
     332           0 :         sub_result()
     333           0 :             : kind{nothing} {};
     334          92 :         sub_result(T* x)
     335          92 :             : kind{singleton}
     336             :         {
     337          92 :             data.singleton = x;
     338          75 :         };
     339         516 :         sub_result(node_t* x)
     340         516 :             : kind{tree}
     341             :         {
     342         516 :             data.tree = x;
     343          88 :         };
     344             :     };
     345             : 
     346             :     template <typename K>
     347             :     sub_result
     348         608 :     do_sub(node_t* node, const K& k, hash_t hash, shift_t shift) const
     349             :     {
     350         608 :         if (shift == max_shift<B>) {
     351           0 :             auto fst = node->collisions();
     352           0 :             auto lst = fst + node->collision_count();
     353           0 :             for (auto cur = fst; cur != lst; ++cur)
     354           0 :                 if (Equal{}(*cur, k))
     355           0 :                     return node->collision_count() > 2
     356             :                                ? node_t::copy_collision_remove(node, cur)
     357          64 :                                : sub_result{fst + (cur == fst)};
     358           0 :             return {};
     359             :         } else {
     360         608 :             auto idx = (hash & (mask<B> << shift)) >> shift;
     361         608 :             auto bit = bitmap_t{1u} << idx;
     362         608 :             if (node->nodemap() & bit) {
     363         161 :                 auto offset = node->children_count(bit);
     364         161 :                 auto result =
     365         161 :                     do_sub(node->children()[offset], k, hash, shift + B);
     366         161 :                 switch (result.kind) {
     367           0 :                 case sub_result::nothing:
     368           0 :                     return {};
     369          92 :                 case sub_result::singleton:
     370          92 :                     return node->datamap() == 0 &&
     371           0 :                                    node->children_count() == 1 && shift > 0
     372          92 :                                ? result
     373             :                                : node_t::copy_inner_replace_inline(
     374         195 :                                      node, bit, offset, *result.data.singleton);
     375          69 :                 case sub_result::tree:
     376             :                     IMMER_TRY {
     377             :                         return node_t::copy_inner_replace(
     378          69 :                             node, offset, result.data.tree);
     379             :                     }
     380           0 :                     IMMER_CATCH (...) {
     381           0 :                         node_t::delete_deep_shift(result.data.tree, shift + B);
     382           0 :                         IMMER_RETHROW;
     383             :                     }
     384             :                 }
     385         447 :             } else if (node->datamap() & bit) {
     386         447 :                 auto offset = node->data_count(bit);
     387         447 :                 auto val    = node->values() + offset;
     388         447 :                 if (Equal{}(*val, k)) {
     389         447 :                     auto nv = node->data_count();
     390         447 :                     if (node->nodemap() || nv > 2)
     391             :                         return node_t::copy_inner_remove_value(
     392         303 :                             node, bit, offset);
     393         144 :                     else if (nv == 2) {
     394          92 :                         return shift > 0 ? sub_result{node->values() + !offset}
     395             :                                          : node_t::make_inner_n(
     396             :                                                0,
     397          13 :                                                node->datamap() & ~bit,
     398         666 :                                                node->values()[!offset]);
     399             :                     } else {
     400          39 :                         assert(shift == 0);
     401          39 :                         return empty();
     402             :                     }
     403             :                 }
     404             :             }
     405           0 :             return {};
     406             :         }
     407             :     }
     408             : 
     409             :     template <typename K>
     410         447 :     champ sub(const K& k) const
     411             :     {
     412         447 :         auto hash = Hash{}(k);
     413         447 :         auto res  = do_sub(root, k, hash, 0);
     414         447 :         switch (res.kind) {
     415           0 :         case sub_result::nothing:
     416         447 :             return *this;
     417         447 :         case sub_result::tree:
     418         447 :             return {res.data.tree, size - 1};
     419           0 :         default:
     420           0 :             IMMER_UNREACHABLE;
     421             :         }
     422             :     }
     423             : 
     424             :     template <typename Eq = Equal>
     425             :     bool equals(const champ& other) const
     426             :     {
     427             :         return size == other.size && equals_tree<Eq>(root, other.root, 0);
     428             :     }
     429             : 
     430             :     template <typename Eq>
     431             :     static bool equals_tree(const node_t* a, const node_t* b, count_t depth)
     432             :     {
     433             :         if (a == b)
     434             :             return true;
     435             :         else if (depth == max_depth<B>) {
     436             :             auto nv = a->collision_count();
     437             :             return nv == b->collision_count() &&
     438             :                    equals_collisions<Eq>(a->collisions(), b->collisions(), nv);
     439             :         } else {
     440             :             if (a->nodemap() != b->nodemap() || a->datamap() != b->datamap())
     441             :                 return false;
     442             :             auto n = a->children_count();
     443             :             for (auto i = count_t{}; i < n; ++i)
     444             :                 if (!equals_tree<Eq>(
     445             :                         a->children()[i], b->children()[i], depth + 1))
     446             :                     return false;
     447             :             auto nv = a->data_count();
     448             :             return !nv || equals_values<Eq>(a->values(), b->values(), nv);
     449             :         }
     450             :     }
     451             : 
     452             :     template <typename Eq>
     453             :     static bool equals_values(const T* a, const T* b, count_t n)
     454             :     {
     455             :         return std::equal(a, a + n, b, Eq{});
     456             :     }
     457             : 
     458             :     template <typename Eq>
     459             :     static bool equals_collisions(const T* a, const T* b, count_t n)
     460             :     {
     461             :         auto ae = a + n;
     462             :         auto be = b + n;
     463             :         for (; a != ae; ++a) {
     464             :             for (auto fst = b; fst != be; ++fst)
     465             :                 if (Eq{}(*a, *fst))
     466             :                     goto good;
     467             :             return false;
     468             :         good:
     469             :             continue;
     470             :         }
     471             :         return true;
     472             :     }
     473             : };
     474             : 
     475             : } // namespace hamts
     476             : } // namespace detail
     477             : } // namespace immer

Generated by: LCOV version 1.14