diff --git a/dev.oid.toml b/dev.oid.toml index bf70202..e72a4cd 100644 --- a/dev.oid.toml +++ b/dev.oid.toml @@ -20,6 +20,7 @@ containers = [ "PWD/types/uniq_ptr_type.toml", "PWD/types/std_map_type.toml", "PWD/types/std_unordered_map_type.toml", + "PWD/types/std_unordered_multimap_type.toml", "PWD/types/pair_type.toml", "PWD/types/stack_container_adapter_type.toml", "PWD/types/queue_container_adapter_type.toml", diff --git a/oi/ContainerTypeEnum.h b/oi/ContainerTypeEnum.h index a218754..a6780e7 100644 --- a/oi/ContainerTypeEnum.h +++ b/oi/ContainerTypeEnum.h @@ -26,6 +26,7 @@ X(LIST_TYPE) \ X(STD_MAP_TYPE) \ X(STD_UNORDERED_MAP_TYPE) \ + X(STD_UNORDERED_MULTIMAP_TYPE) \ X(MAP_SEQ_TYPE) \ X(BY_MULTI_QRT_TYPE) \ X(F14_MAP) \ diff --git a/oi/TreeBuilder.cpp b/oi/TreeBuilder.cpp index 7aff17d..802beba 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 STD_UNORDERED_MULTIMAP_TYPE: case STD_UNORDERED_MAP_TYPE: { // Account for node overhead containerStats.elementStaticSize += next(); diff --git a/test/ci.oid.toml b/test/ci.oid.toml index 5ac1a80..9c73202 100644 --- a/test/ci.oid.toml +++ b/test/ci.oid.toml @@ -16,6 +16,7 @@ containers = [ "../types/uniq_ptr_type.toml", "../types/std_map_type.toml", "../types/std_unordered_map_type.toml", + "../types/std_unordered_multimap_type.toml", "../types/pair_type.toml", "../types/stack_container_adapter_type.toml", "../types/queue_container_adapter_type.toml", diff --git a/test/integration/std_unordered_multimap_custom_operator.toml b/test/integration/std_unordered_multimap_custom_operator.toml new file mode 100644 index 0000000..37fe213 --- /dev/null +++ b/test/integration/std_unordered_multimap_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_multimap m1; + std::unordered_multimap> m2; + std::unordered_multimap, CustomEqualFnInt<8>> m3; + std::unordered_multimap, CustomEqualFnInt<8>> m4; + }; +''' +includes = ["unordered_map"] + +[cases] + [cases.a] + param_types = ["const Foo&"] + setup = ''' + Foo foo; + + for (int i = 0; i < 3; i++) { + foo.m1.emplace(i, i * 10); + } + + for (int i = 0; i < 5; i++) { + foo.m2.emplace(i, i * 10); + } + + for (int i = 0; i < 7; i++) { + foo.m3.emplace(i, i * 10); + } + + for (int i = 0; i < 9; i++) { + foo.m4.emplace(i, i * 10); + } + + return {foo}; + ''' + expect_json = '''[{ + "staticSize":480, + "dynamicSize":1184, + "members":[ + {"name":"m1", "staticSize":56, "dynamicSize":200, "length":3, "capacity":3, "elementStaticSize":32}, + {"name":"m2", "staticSize":120, "dynamicSize":264, "length":5, "capacity":5, "elementStaticSize":32}, + {"name":"m3", "staticSize":120, "dynamicSize":328, "length":7, "capacity":7, "elementStaticSize":32}, + {"name":"m4", "staticSize":184, "dynamicSize":392, "length":9, "capacity":9, "elementStaticSize":32} + ]}]''' + 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/std_unordered_multimap_type.toml b/types/std_unordered_multimap_type.toml new file mode 100644 index 0000000..619cb94 --- /dev/null +++ b/types/std_unordered_multimap_type.toml @@ -0,0 +1,164 @@ +[info] +type_name = "std::unordered_multimap" +stub_template_params = [2,3,4] +ctype = "STD_UNORDERED_MULTIMAP_TYPE" +header = "unordered_map" + +# Old: +typeName = "std::unordered_multimap<" +ns = ["namespace std"] +numTemplateParams = 2 +replaceTemplateParamIndex = [2, 3] +allocatorIndex = 4 + +[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%) + (nodeSize * numElems) + (bucketCount * sizeof(uintptr_t))); + + SAVE_DATA((uintptr_t)nodeSize); + SAVE_DATA((uintptr_t)bucketCount); + SAVE_DATA((uintptr_t)numElems); + + for (auto const& it : container) + { + getSizeType(it.first, returnArg); + getSizeType(it.second, returnArg); + } +} +""" + +handler = """ +template +struct TypeHandler> { + using type = types::st::Pair, + types::st::Pair, + types::st::List::type, + typename TypeHandler::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 (const auto& it : container) { + tail = tail.delegate([&it](auto ret) { + return OIInternal::getSizeType(it.second, ret.delegate([&it](auto ret) { + return OIInternal::getSizeType(it.first, 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, + typename TypeHandler::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_value_type = std::pair; +using OI_Hash_node = + std::__detail::_Hash_node< + OI_value_type, + std::__cache_default::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 std::array element_fields{ + make_field("key"), + make_field("value"), +}; + +static constexpr auto element = inst::Field{ + element_size, + element_size - sizeof(T0) - sizeof(T1), + "[]", + std::array{}, + element_fields, + std::array{}, +}; + +for (size_t i = 0; i < list.length; i++) + ins.emplace(element); +"""