From deb7dcdf1c74020ec409976e235b3d84169a3dee Mon Sep 17 00:00:00 2001 From: Thierry Treyer Date: Tue, 29 Aug 2023 10:19:23 -0700 Subject: [PATCH] Implement Container V2 for std::unordered_multiset --- dev.oid.toml | 1 + oi/ContainerTypeEnum.h | 1 + oi/TreeBuilder.cpp | 1 + test/ci.oid.toml | 1 + ...td_unordered_multiset_custom_operator.toml | 74 ++++++++++ types/unordered_multiset_type.toml | 137 ++++++++++++++++++ 6 files changed, 215 insertions(+) create mode 100644 test/integration/std_unordered_multiset_custom_operator.toml create mode 100644 types/unordered_multiset_type.toml diff --git a/dev.oid.toml b/dev.oid.toml index e72a4cd..709b6b8 100644 --- a/dev.oid.toml +++ b/dev.oid.toml @@ -12,6 +12,7 @@ containers = [ "PWD/types/set_type.toml", "PWD/types/multi_set_type.toml", "PWD/types/unordered_set_type.toml", + "PWD/types/unordered_multiset_type.toml", "PWD/types/seq_type.toml", "PWD/types/list_type.toml", "PWD/types/cxx11_list_type.toml", diff --git a/oi/ContainerTypeEnum.h b/oi/ContainerTypeEnum.h index a6780e7..943c38c 100644 --- a/oi/ContainerTypeEnum.h +++ b/oi/ContainerTypeEnum.h @@ -22,6 +22,7 @@ X(SET_TYPE) \ X(MULTI_SET_TYPE) \ X(UNORDERED_SET_TYPE) \ + X(UNORDERED_MULTISET_TYPE) \ X(SEQ_TYPE) \ X(LIST_TYPE) \ X(STD_MAP_TYPE) \ diff --git a/oi/TreeBuilder.cpp b/oi/TreeBuilder.cpp index 802beba..9f934e4 100644 --- a/oi/TreeBuilder.cpp +++ b/oi/TreeBuilder.cpp @@ -822,6 +822,7 @@ void TreeBuilder::processContainer(const Variable& variable, Node& node) { containerStats.length = containerStats.capacity = next(); break; case UNORDERED_SET_TYPE: + case UNORDERED_MULTISET_TYPE: case STD_UNORDERED_MULTIMAP_TYPE: case STD_UNORDERED_MAP_TYPE: { // Account for node overhead diff --git a/test/ci.oid.toml b/test/ci.oid.toml index 9c73202..a3eea01 100644 --- a/test/ci.oid.toml +++ b/test/ci.oid.toml @@ -8,6 +8,7 @@ containers = [ "../types/set_type.toml", "../types/multi_set_type.toml", "../types/unordered_set_type.toml", + "../types/unordered_multiset_type.toml", "../types/seq_type.toml", "../types/list_type.toml", "../types/cxx11_list_type.toml", diff --git a/test/integration/std_unordered_multiset_custom_operator.toml b/test/integration/std_unordered_multiset_custom_operator.toml new file mode 100644 index 0000000..ef08dcb --- /dev/null +++ b/test/integration/std_unordered_multiset_custom_operator.toml @@ -0,0 +1,74 @@ +definitions = ''' + + template + class CustomIntHasher + { + double d[N]; + public: + size_t operator() (int const& key) const + { + return std::hash{}(key); + } + }; + + template + class CustomEqualFnInt + { + double d[N]; + public: + bool operator() (int const& t1, int const& t2) const + { + return t1 == t2; + } + }; + + struct Foo { + std::unordered_multiset m1; + std::unordered_multiset> m2; + std::unordered_multiset, CustomEqualFnInt<8>> m3; + std::unordered_multiset, CustomEqualFnInt<8>> m4; + }; +''' +includes = ["unordered_set"] + +[cases] + [cases.a] + param_types = ["const Foo&"] + setup = ''' + Foo foo; + + for (int i = 0; i < 3; i++) { + foo.m1.insert(i); + } + + for (int i = 0; i < 5; i++) { + foo.m2.insert(i); + } + + for (int i = 0; i < 7; i++) { + foo.m3.insert(i); + } + + for (int i = 0; i < 9; i++) { + foo.m4.insert(i); + } + + return {foo}; + ''' + expect_json = '''[{ + "staticSize":480, + "dynamicSize":704, + "members":[ + {"name":"m1", "staticSize":56, "dynamicSize":140, "length":3, "capacity":3, "elementStaticSize":12}, + {"name":"m2", "staticSize":120, "dynamicSize":164, "length":5, "capacity":5, "elementStaticSize":12}, + {"name":"m3", "staticSize":120, "dynamicSize":188, "length":7, "capacity":7, "elementStaticSize":12}, + {"name":"m4", "staticSize":184, "dynamicSize":212, "length":9, "capacity":9, "elementStaticSize":12} + ]}]''' + expect_json_v2 = '''[{ + "staticSize":480, + "members":[ + {"name":"m1", "staticSize":56, "exclusiveSize":220, "length":3, "capacity":3}, + {"name":"m2", "staticSize":120, "exclusiveSize":324, "length":5, "capacity":5}, + {"name":"m3", "staticSize":120, "exclusiveSize":364, "length":7, "capacity":7}, + {"name":"m4", "staticSize":184, "exclusiveSize":468, "length":9, "capacity":9} + ]}]''' diff --git a/types/unordered_multiset_type.toml b/types/unordered_multiset_type.toml new file mode 100644 index 0000000..b04e936 --- /dev/null +++ b/types/unordered_multiset_type.toml @@ -0,0 +1,137 @@ +[info] +type_name = "std::unordered_multiset" +stub_template_params = [1,2,3] +ctype = "UNORDERED_MULTISET_TYPE" +header = "unordered_set" + +# Old: +typeName = "std::unordered_multiset<" +ns = ["namespace std"] +numTemplateParams = 1 +replaceTemplateParamIndex = [1, 2] +allocatorIndex = 3 + +[codegen] +decl = """ +template +void getSizeType(const %1% &container, size_t& returnArg); +""" + +func = """ +template +void getSizeType(const %1% &container, size_t& returnArg) +{ + constexpr size_t nodeSize = sizeof(typename %1%::node_type); + size_t bucketCount = container.bucket_count(); + size_t numElems = container.size(); + SAVE_SIZE(sizeof(%1%) + (numElems * nodeSize) + (bucketCount * sizeof(uintptr_t))); + + SAVE_DATA((uintptr_t)nodeSize); + SAVE_DATA((uintptr_t)bucketCount); + SAVE_DATA((uintptr_t)numElems); + + // The double ampersand is needed otherwise this loop doesn't work with vector + for (auto&& it: container) { + getSizeType(it, returnArg); + } +} +""" + +handler = """ +template +struct TypeHandler> { + using type = types::st::Pair< + DB, types::st::VarInt, + types::st::Pair< + DB, types::st::VarInt, + types::st::List::type>>>; + + static types::st::Unit getSizeType( + const %1%& container, + typename TypeHandler>::type returnArg) { + constexpr size_t nodeSize = sizeof(typename %1%::node_type); + + auto tail = returnArg.write(nodeSize) + .write(container.bucket_count()) + .write(container.size()); + + // The double ampersand is needed otherwise this loop doesn't work with + // vector + for (auto&& it : container) { + tail = tail.delegate([&it](auto ret) { + return OIInternal::getSizeType(it, ret); + }); + } + + return tail.finish(); + } +}; +""" + +traversal_func = """ +auto tail = returnArg + .write((uintptr_t)&container) + .write(container.bucket_count()) + .write(container.size()); + +for (const auto &it : container) { + tail = tail.delegate([&it](auto ret) { + return OIInternal::getSizeType(it, ret); + }); +} + +return tail.finish(); +""" + +[[codegen.processor]] +type = "types::st::VarInt" +func = """ +el.pointer = std::get(d.val).value; +""" + +[[codegen.processor]] +type = "types::st::VarInt" +func = """ +// Using the container's capacity to temporarily store the number of buckets +// TODO: Is there another way to pass a value across processors? +el.container_stats.emplace(result::Element::ContainerStats { + .capacity = std::get(d.val).value, +}); +""" + +[[codegen.processor]] +type = "types::st::List::type>" +func = """ +#ifdef __GLIBCXX__ +/* Use libstdc++ implementation __details to compute the size of Nodes and Buckets. + * + * See the source of : + * https://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen/a00536_source.html + */ +using OI_Hash_node = + std::__detail::_Hash_node::value>; + +using OI_bucket = std::__detail::_Hash_node_base; + +static constexpr size_t element_size = sizeof(OI_Hash_node); +static constexpr size_t bucket_size = sizeof(OI_bucket); +#else +static_assert(false && "No known element_size for sets. See types/set_type.toml"); +#endif + +auto list = std::get(d.val); +// Reading the bucket count that was stored in `capacity` by the processor above. +size_t bucket_count = el.container_stats->capacity; +el.exclusive_size += bucket_count * bucket_size; +el.exclusive_size += list.length * (element_size - sizeof(T0)); + +// Overwrite the bucket count stored in `capacity` with the actual container's values. +el.container_stats.emplace(result::Element::ContainerStats { + .capacity = list.length, + .length = list.length, +}); + +static constexpr auto childField = make_field("[]"); +for (size_t i = 0; i < list.length; i++) + ins.emplace(childField); +"""