Skip to content

Commit f09f62c

Browse files
committed
Add test for std::unordered_multimap
1 parent 55daad0 commit f09f62c

File tree

11 files changed

+404
-0
lines changed

11 files changed

+404
-0
lines changed

types/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
* [`tuple`](tuple): `std::tuple` with different element types
1616
* [`unique_ptr`](unique_ptr): `std::unique_ptr` with different element types
1717
* [`unordered_map`](unordered_map): `std::unordered_map` with all `[Split]Index{32,64}` column types
18+
* [`unordered_multimap`](unordered_multimap): `std::unordered_multimap` with all `[Split]Index{32,64}` column types
1819
* [`unordered_multiset`](unordered_multiset): `std::unordered_multiset` with all `[Split]Index{32,64}` column types
1920
* [`unordered_set`](unordered_set): `std::unordered_set` with all `[Split]Index{32,64}` column types
2021
* [`user`](user): user-defined types, such as classes and enums

types/unordered_multimap/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# `std::unordered_map`
2+
3+
- [`fundamental`](fundamental): `std::unordered_map<std::string, ::int32_t>`
4+
- [`nested`](nested): `std::unordered_map<std::string, std::unordered_map<std::string, std::int32_t>>`
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# `std::unordered_multimap<std::string, std::int32_t>`
2+
3+
## Fields
4+
5+
- `[Split]Index{32,64}`
6+
7+
with the corresponding column type for the offset column of the collection parent field.
8+
All child fields use the default column types for `std::string` (`SplitIndex64` for the principal column, `Char` for the second column) and `std::int32_t` (`SplitInt32`).
9+
10+
## Entries
11+
12+
1. Single-element maps, with ascending values for both key and value
13+
2. Empty maps
14+
3. Increasing number of elements in the map:
15+
one key-value pair in the first field, two key-value pairs in the second field, etc.
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#include <ROOT/REntry.hxx>
2+
#include <ROOT/RNTupleReader.hxx>
3+
4+
using ROOT::Experimental::REntry;
5+
using ROOT::Experimental::RNTupleReader;
6+
7+
#include <cstdint>
8+
#include <fstream>
9+
#include <ostream>
10+
#include <string>
11+
#include <string_view>
12+
#include <unordered_map>
13+
14+
using UnorderedMultimap = std::unordered_multimap<std::string, std::int32_t>;
15+
16+
static void PrintMultimapValue(const REntry &entry, std::string_view name,
17+
std::ostream &os, bool last = false) {
18+
UnorderedMultimap &item = *entry.GetPtr<UnorderedMultimap>(name);
19+
20+
std::vector<
21+
std::pair<UnorderedMultimap::key_type, UnorderedMultimap::mapped_type>>
22+
itemSorted(item.begin(), item.end());
23+
std::sort(itemSorted.begin(), itemSorted.end());
24+
25+
os << " \"" << name << "\": {";
26+
bool first = true;
27+
for (auto &[key, value] : itemSorted) {
28+
if (first) {
29+
first = false;
30+
} else {
31+
os << ",";
32+
}
33+
os << "\n \"" << key << "\": " << value;
34+
}
35+
if (!item.empty()) {
36+
os << "\n ";
37+
}
38+
os << "}";
39+
if (!last) {
40+
os << ",";
41+
}
42+
os << "\n";
43+
}
44+
45+
void read(
46+
std::string_view input = "types.unordered_multimap.fundamental.root",
47+
std::string_view output = "types.unordered_multimap.fundamental.json") {
48+
std::ofstream os(std::string{output});
49+
os << "[\n";
50+
51+
auto reader = RNTupleReader::Open("ntpl", input);
52+
auto &entry = reader->GetModel().GetDefaultEntry();
53+
bool first = true;
54+
for (auto index : *reader) {
55+
reader->LoadEntry(index);
56+
57+
if (first) {
58+
first = false;
59+
} else {
60+
os << ",\n";
61+
}
62+
os << " {\n";
63+
64+
PrintMultimapValue(entry, "Index32", os);
65+
PrintMultimapValue(entry, "Index64", os);
66+
PrintMultimapValue(entry, "SplitIndex32", os);
67+
PrintMultimapValue(entry, "SplitIndex64", os, /*last=*/true);
68+
69+
os << " }";
70+
// Newline is intentionally missing, may need to print a comma before the
71+
// next entry.
72+
}
73+
os << "\n";
74+
os << "]\n";
75+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#include <ROOT/RField.hxx>
2+
#include <ROOT/RNTupleModel.hxx>
3+
#include <ROOT/RNTupleUtil.hxx>
4+
#include <ROOT/RNTupleWriteOptions.hxx>
5+
#include <ROOT/RNTupleWriter.hxx>
6+
7+
using ROOT::Experimental::EColumnType;
8+
using ROOT::Experimental::RField;
9+
using ROOT::Experimental::RNTupleModel;
10+
using ROOT::Experimental::RNTupleWriteOptions;
11+
using ROOT::Experimental::RNTupleWriter;
12+
13+
#include <cstdint>
14+
#include <memory>
15+
#include <string_view>
16+
#include <unordered_map>
17+
18+
using UnorderedMultimap = std::unordered_multimap<std::string, std::int32_t>;
19+
20+
static std::shared_ptr<UnorderedMultimap> MakeMultimapField(RNTupleModel &model,
21+
std::string_view name,
22+
EColumnType indexType) {
23+
auto field = std::make_unique<RField<UnorderedMultimap>>(name);
24+
field->SetColumnRepresentatives({{indexType}});
25+
model.AddField(std::move(field));
26+
return model.GetDefaultEntry().GetPtr<UnorderedMultimap>(name);
27+
}
28+
29+
void write(std::string_view filename = "types.unordered_multimap.fundamental.root") {
30+
auto model = RNTupleModel::Create();
31+
32+
// Non-split index encoding
33+
auto Index32 = MakeMultimapField(*model, "Index32", EColumnType::kIndex32);
34+
auto Index64 = MakeMultimapField(*model, "Index64", EColumnType::kIndex64);
35+
36+
// Split index encoding
37+
auto SplitIndex32 =
38+
MakeMultimapField(*model, "SplitIndex32", EColumnType::kSplitIndex32);
39+
auto SplitIndex64 =
40+
MakeMultimapField(*model, "SplitIndex64", EColumnType::kSplitIndex64);
41+
42+
RNTupleWriteOptions options;
43+
options.SetCompression(0);
44+
auto writer =
45+
RNTupleWriter::Recreate(std::move(model), "ntpl", filename, options);
46+
47+
// First entry: single-element maps, with ascending values
48+
*Index32 = {{"a", 1}};
49+
*Index64 = {{"b", 2}};
50+
*SplitIndex32 = {{"c", 3}};
51+
*SplitIndex64 = {{"d", 4}};
52+
writer->Fill();
53+
54+
// Second entry: empty maps
55+
*Index32 = {};
56+
*Index64 = {};
57+
*SplitIndex32 = {};
58+
*SplitIndex64 = {};
59+
writer->Fill();
60+
61+
// Third entry: increasing number of elements in the map
62+
*Index32 = {{"a", 1}};
63+
*Index64 = {{"b", 2}, {"c", 3}};
64+
*SplitIndex32 = {{"d", 4}, {"e", 5}, {"f", 6}};
65+
*SplitIndex64 = {{"g", 7}, {"h", 8}, {"i", 9}, {"j", 10}};
66+
writer->Fill();
67+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#include <cstdint>
2+
#include <unordered_map>
3+
4+
#ifdef __CLING__
5+
#pragma link C++ class std::unordered_multimap<std::string, std::unordered_multimap<std::string, std::int32_t>>+;
6+
#endif
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
ROOT_CONFIG ?= $(shell which root-config)
2+
ifeq ($(ROOT_CONFIG),)
3+
$(error Could not find root-config)
4+
endif
5+
ROOTCLING ?= $(shell which rootcling)
6+
ifeq ($(ROOTCLING),)
7+
$(error Could not find rootcling)
8+
endif
9+
10+
CXX := $(shell $(ROOT_CONFIG) --cxx)
11+
CXXFLAGS_ROOT := $(shell $(ROOT_CONFIG) --cflags)
12+
CXXFLAGS := -Wall $(CXXFLAGS_ROOT)
13+
LDFLAGS := $(shell $(ROOT_CONFIG) --libs)
14+
15+
.PHONY: all clean
16+
17+
all: libNestedUnorderedMultimap.so
18+
19+
NestedUnorderedMultimap.cxx: NestedUnorderedMultimap.hxx LinkDef.h
20+
$(ROOTCLING) -f $@ $^
21+
22+
libNestedUnorderedMultimap.so: NestedUnorderedMultimap.cxx
23+
$(CXX) -shared -fPIC -o $@ $^ $(CXXFLAGS) $(LDFLAGS)
24+
25+
clean:
26+
$(RM) NestedUnorderedMultimap.cxx NestedUnorderedMultimap_rdict.pcm libNestedUnorderedMultimap.so
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#pragma once
2+
3+
#include <cstdint>
4+
#include <unordered_map>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# `std::unordered_multimap<std::string, std::unordered_multimap<std::string, std::int32_t>>`
2+
3+
## Fields
4+
5+
- `[Split]Index{32,64}`
6+
7+
with the corresponding column type for the offset column of the two collection parent fields.
8+
All child fields use the default column types for `std::string` (`SplitIndex64` for the principal column, `Char` for the second column) and `std::int32_t` (`SplitInt32`).
9+
10+
## Entries
11+
12+
1. Single-element maps, with ascending values for both key and value
13+
2. Empty maps
14+
3. Increasing number of elements in the outer map, with arbitrary lengths of the inner maps
15+
16+
## Dictionaries
17+
18+
These tests require ROOT dictionaries, which can be generated with the provided `Makefile` in this directory. This will create a `libNestedUnorderedMultimap` shared object.
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
#include <ROOT/REntry.hxx>
2+
#include <ROOT/RNTupleReader.hxx>
3+
4+
using ROOT::Experimental::REntry;
5+
using ROOT::Experimental::RNTupleReader;
6+
7+
#include <TSystem.h>
8+
9+
#include <cstdint>
10+
#include <filesystem>
11+
#include <fstream>
12+
#include <ostream>
13+
#include <string>
14+
#include <string_view>
15+
#include <unordered_map>
16+
17+
using UnorderedMultimap =
18+
std::unordered_multimap<std::string,
19+
std::unordered_multimap<std::string, std::int32_t>>;
20+
21+
static void PrintNestedUnorderedMultimapValue(const REntry &entry,
22+
std::string_view name,
23+
std::ostream &os,
24+
bool last = false) {
25+
UnorderedMultimap &item = *entry.GetPtr<UnorderedMultimap>(name);
26+
27+
std::vector<std::pair<
28+
UnorderedMultimap::key_type,
29+
std::vector<std::pair<UnorderedMultimap::mapped_type::key_type,
30+
UnorderedMultimap::mapped_type::mapped_type>>>>
31+
itemSorted;
32+
for (auto &[key, inner] : item) {
33+
std::vector<std::pair<UnorderedMultimap::mapped_type::key_type,
34+
UnorderedMultimap::mapped_type::mapped_type>>
35+
innerSorted(inner.begin(), inner.end());
36+
std::sort(innerSorted.begin(), innerSorted.end());
37+
itemSorted.emplace_back(std::make_pair(key, innerSorted));
38+
}
39+
std::sort(itemSorted.begin(), itemSorted.end());
40+
41+
os << " \"" << name << "\": {";
42+
bool outerFirst = true;
43+
for (auto &[key, inner] : itemSorted) {
44+
if (outerFirst) {
45+
outerFirst = false;
46+
} else {
47+
os << ",";
48+
}
49+
os << "\n \"" << key << "\": {";
50+
bool innerFirst = true;
51+
for (auto &[innerKey, value] : inner) {
52+
if (innerFirst) {
53+
innerFirst = false;
54+
} else {
55+
os << ",";
56+
}
57+
os << "\n \"" << innerKey << "\": " << value;
58+
}
59+
if (!inner.empty()) {
60+
os << "\n ";
61+
}
62+
os << "}";
63+
}
64+
if (!item.empty()) {
65+
os << "\n ";
66+
}
67+
os << "}";
68+
if (!last) {
69+
os << ",";
70+
}
71+
os << "\n";
72+
}
73+
74+
void read(std::string_view input = "types.unordered_multimap.nested.root",
75+
std::string_view output = "types.unordered_multimap.nested.json") {
76+
if (gSystem->Load("libNestedUnorderedMultimap") == -1)
77+
throw std::runtime_error("could not find the required ROOT dictionaries, "
78+
"please make sure to run `make` first");
79+
80+
std::ofstream os(std::string{output});
81+
os << "[\n";
82+
83+
auto reader = RNTupleReader::Open("ntpl", input);
84+
auto &entry = reader->GetModel().GetDefaultEntry();
85+
bool first = true;
86+
for (auto index : *reader) {
87+
reader->LoadEntry(index);
88+
89+
if (first) {
90+
first = false;
91+
} else {
92+
os << ",\n";
93+
}
94+
os << " {\n";
95+
96+
PrintNestedUnorderedMultimapValue(entry, "Index32", os);
97+
PrintNestedUnorderedMultimapValue(entry, "Index64", os);
98+
PrintNestedUnorderedMultimapValue(entry, "SplitIndex32", os);
99+
PrintNestedUnorderedMultimapValue(entry, "SplitIndex64", os, /*last=*/true);
100+
101+
os << " }";
102+
// Newline is intentionally missing, may need to print a comma before the
103+
// next entry.
104+
}
105+
os << "\n";
106+
os << "]\n";
107+
}

0 commit comments

Comments
 (0)