diff --git a/types/README.md b/types/README.md index 0c6525a..3c49e6c 100644 --- a/types/README.md +++ b/types/README.md @@ -4,6 +4,8 @@ * [`atomic`](atomic): `std::atomic` * [`bitset`](bitset): `std::bitset` * [`fundamental`](fundamental): fundamental column types + * [`map`](map): `std::map` with all `[Split]Index{32,64}` column types + * [`multimap`](multimap): `std::multimap` with all `[Split]Index{32,64}` column types * [`multiset`](multiset): `std::multiset` with all `[Split]Index{32,64}` column types * [`optional`](optional): `std::optional` with different element types * [`pair`](pair): `std::pair` with different element types @@ -12,6 +14,8 @@ * [`string`](string): `std::string` with all `[Split]Index{32,64}` column types * [`tuple`](tuple): `std::tuple` with different element types * [`unique_ptr`](unique_ptr): `std::unique_ptr` with different element types + * [`unordered_map`](unordered_map): `std::unordered_map` with all `[Split]Index{32,64}` column types + * [`unordered_multimap`](unordered_multimap): `std::unordered_multimap` with all `[Split]Index{32,64}` column types * [`unordered_multiset`](unordered_multiset): `std::unordered_multiset` with all `[Split]Index{32,64}` column types * [`unordered_set`](unordered_set): `std::unordered_set` with all `[Split]Index{32,64}` column types * [`user`](user): user-defined types, such as classes and enums @@ -39,8 +43,8 @@ If `C` is a container type with a single item field, we test the following nesti * `C>` If `M` is an dictionary container type, we test the following nestings: - * `M` - * `M, M>` + * `M` + * `M, M>` ### Record Fields diff --git a/types/map/README.md b/types/map/README.md new file mode 100644 index 0000000..e17152e --- /dev/null +++ b/types/map/README.md @@ -0,0 +1,4 @@ +# `std::map` + +- [`fundamental`](fundamental): `std::map` +- [`nested`](nested): `std::map>` diff --git a/types/map/fundamental/README.md b/types/map/fundamental/README.md new file mode 100644 index 0000000..081bff2 --- /dev/null +++ b/types/map/fundamental/README.md @@ -0,0 +1,15 @@ +# `std::map` + +## Fields + +- `[Split]Index{32,64}` + +with the corresponding column type for the offset column of the collection parent field. +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`). + +## Entries + +1. Single-element maps, with ascending values for both key and value +2. Empty maps +3. Increasing number of elements in the map: + one key-value pair in the first field, two key-value pairs in the second field, etc. diff --git a/types/map/fundamental/read.C b/types/map/fundamental/read.C new file mode 100644 index 0000000..6197876 --- /dev/null +++ b/types/map/fundamental/read.C @@ -0,0 +1,68 @@ +#include +#include + +using ROOT::Experimental::REntry; +using ROOT::Experimental::RNTupleReader; + +#include +#include +#include +#include +#include +#include + +using Map = std::map; + +static void PrintMapValue(const REntry &entry, std::string_view name, + std::ostream &os, bool last = false) { + Map &item = *entry.GetPtr(name); + os << " \"" << name << "\": {"; + bool first = true; + for (auto &[key, value] : item) { + if (first) { + first = false; + } else { + os << ","; + } + os << "\n \"" << key << "\": " << value; + } + if (!item.empty()) { + os << "\n "; + } + os << "}"; + if (!last) { + os << ","; + } + os << "\n"; +} + +void read(std::string_view input = "types.map.fundamental.root", + std::string_view output = "types.map.fundamental.json") { + std::ofstream os(std::string{output}); + os << "[\n"; + + auto reader = RNTupleReader::Open("ntpl", input); + auto &entry = reader->GetModel().GetDefaultEntry(); + bool first = true; + for (auto index : *reader) { + reader->LoadEntry(index); + + if (first) { + first = false; + } else { + os << ",\n"; + } + os << " {\n"; + + PrintMapValue(entry, "Index32", os); + PrintMapValue(entry, "Index64", os); + PrintMapValue(entry, "SplitIndex32", os); + PrintMapValue(entry, "SplitIndex64", os, /*last=*/true); + + os << " }"; + // Newline is intentionally missing, may need to print a comma before the + // next entry. + } + os << "\n"; + os << "]\n"; +} diff --git a/types/map/fundamental/write.C b/types/map/fundamental/write.C new file mode 100644 index 0000000..5447f7b --- /dev/null +++ b/types/map/fundamental/write.C @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include + +using ROOT::Experimental::EColumnType; +using ROOT::Experimental::RField; +using ROOT::Experimental::RNTupleModel; +using ROOT::Experimental::RNTupleWriteOptions; +using ROOT::Experimental::RNTupleWriter; + +#include +#include +#include +#include + +using Map = std::map; + +static std::shared_ptr MakeMapField(RNTupleModel &model, + std::string_view name, + EColumnType indexType) { + auto field = std::make_unique>(name); + field->SetColumnRepresentatives({{indexType}}); + model.AddField(std::move(field)); + return model.GetDefaultEntry().GetPtr(name); +} + +void write(std::string_view filename = "types.map.fundamental.root") { + auto model = RNTupleModel::Create(); + + // Non-split index encoding + auto Index32 = MakeMapField(*model, "Index32", EColumnType::kIndex32); + auto Index64 = MakeMapField(*model, "Index64", EColumnType::kIndex64); + + // Split index encoding + auto SplitIndex32 = + MakeMapField(*model, "SplitIndex32", EColumnType::kSplitIndex32); + auto SplitIndex64 = + MakeMapField(*model, "SplitIndex64", EColumnType::kSplitIndex64); + + RNTupleWriteOptions options; + options.SetCompression(0); + auto writer = + RNTupleWriter::Recreate(std::move(model), "ntpl", filename, options); + + // First entry: single-element maps, with ascending values + *Index32 = {{"a", 1}}; + *Index64 = {{"b", 2}}; + *SplitIndex32 = {{"c", 3}}; + *SplitIndex64 = {{"d", 4}}; + writer->Fill(); + + // Second entry: empty maps + *Index32 = {}; + *Index64 = {}; + *SplitIndex32 = {}; + *SplitIndex64 = {}; + writer->Fill(); + + // Third entry: increasing number of elements in the map + *Index32 = {{"a", 1}}; + *Index64 = {{"b", 2}, {"c", 3}}; + *SplitIndex32 = {{"d", 4}, {"e", 5}, {"f", 6}}; + *SplitIndex64 = {{"g", 7}, {"h", 8}, {"i", 9}, {"j", 10}}; + writer->Fill(); +} diff --git a/types/map/nested/LinkDef.h b/types/map/nested/LinkDef.h new file mode 100644 index 0000000..a258912 --- /dev/null +++ b/types/map/nested/LinkDef.h @@ -0,0 +1,6 @@ +#include +#include + +#ifdef __CLING__ +#pragma link C++ class std::map>+; +#endif diff --git a/types/map/nested/Makefile b/types/map/nested/Makefile new file mode 100644 index 0000000..f401079 --- /dev/null +++ b/types/map/nested/Makefile @@ -0,0 +1,26 @@ +ROOT_CONFIG ?= $(shell which root-config) +ifeq ($(ROOT_CONFIG),) +$(error Could not find root-config) +endif +ROOTCLING ?= $(shell which rootcling) +ifeq ($(ROOTCLING),) +$(error Could not find rootcling) +endif + +CXX := $(shell $(ROOT_CONFIG) --cxx) +CXXFLAGS_ROOT := $(shell $(ROOT_CONFIG) --cflags) +CXXFLAGS := -Wall $(CXXFLAGS_ROOT) +LDFLAGS := $(shell $(ROOT_CONFIG) --libs) + +.PHONY: all clean + +all: libNestedMap.so + +NestedMap.cxx: NestedMap.hxx LinkDef.h + $(ROOTCLING) -f $@ $^ + +libNestedMap.so: NestedMap.cxx + $(CXX) -shared -fPIC -o $@ $^ $(CXXFLAGS) $(LDFLAGS) + +clean: + $(RM) NestedMap.cxx NestedMap_rdict.pcm libNestedMap.so diff --git a/types/map/nested/NestedMap.hxx b/types/map/nested/NestedMap.hxx new file mode 100644 index 0000000..cf43459 --- /dev/null +++ b/types/map/nested/NestedMap.hxx @@ -0,0 +1,4 @@ +#pragma once + +#include +#include diff --git a/types/map/nested/README.md b/types/map/nested/README.md new file mode 100644 index 0000000..4694d75 --- /dev/null +++ b/types/map/nested/README.md @@ -0,0 +1,18 @@ +# `std::map>` + +## Fields + +- `[Split]Index{32,64}` + +with the corresponding column type for the offset column of the two collection parent fields. +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`). + +## Entries + +1. Single-element maps, with ascending values for both key and value +2. Empty maps +3. Increasing number of elements in the outer map, with arbitrary lengths of the inner maps + +## Dictionaries + +These tests require ROOT dictionaries, which can be generated with the provided `Makefile` in this directory. This will create a `libNestedMap` shared object. diff --git a/types/map/nested/read.C b/types/map/nested/read.C new file mode 100644 index 0000000..bd72266 --- /dev/null +++ b/types/map/nested/read.C @@ -0,0 +1,88 @@ +#include +#include + +using ROOT::Experimental::REntry; +using ROOT::Experimental::RNTupleReader; + +#include + +#include +#include +#include +#include +#include +#include +#include + +using Map = std::map>; + +static void PrintNestedMapValue(const REntry &entry, std::string_view name, + std::ostream &os, bool last = false) { + Map &item = *entry.GetPtr(name); + os << " \"" << name << "\": {"; + bool outerFirst = true; + for (auto &[key, inner] : item) { + if (outerFirst) { + outerFirst = false; + } else { + os << ","; + } + os << "\n \"" << key << "\": {"; + bool innerFirst = true; + for (auto &[innerKey, value] : inner) { + if (innerFirst) { + innerFirst = false; + } else { + os << ","; + } + os << "\n \"" << innerKey << "\": " << value; + } + if (!inner.empty()) { + os << "\n "; + } + os << "}"; + } + if (!item.empty()) { + os << "\n "; + } + os << "}"; + if (!last) { + os << ","; + } + os << "\n"; +} + +void read(std::string_view input = "types.map.nested.root", + std::string_view output = "types.map.nested.json") { + if (gSystem->Load("libNestedMap") == -1) + throw std::runtime_error("could not find the required ROOT dictionaries, " + "please make sure to run `make` first"); + + std::ofstream os(std::string{output}); + os << "[\n"; + + auto reader = RNTupleReader::Open("ntpl", input); + auto &entry = reader->GetModel().GetDefaultEntry(); + bool first = true; + for (auto index : *reader) { + reader->LoadEntry(index); + + if (first) { + first = false; + } else { + os << ",\n"; + } + os << " {\n"; + + PrintNestedMapValue(entry, "Index32", os); + PrintNestedMapValue(entry, "Index64", os); + PrintNestedMapValue(entry, "SplitIndex32", os); + PrintNestedMapValue(entry, "SplitIndex64", os, /*last=*/true); + + os << " }"; + // Newline is intentionally missing, may need to print a comma before the + // next entry. + } + os << "\n"; + os << "]\n"; +} diff --git a/types/map/nested/write.C b/types/map/nested/write.C new file mode 100644 index 0000000..b63a83f --- /dev/null +++ b/types/map/nested/write.C @@ -0,0 +1,80 @@ +#include +#include +#include +#include +#include + +using ROOT::Experimental::EColumnType; +using ROOT::Experimental::RField; +using ROOT::Experimental::RNTupleModel; +using ROOT::Experimental::RNTupleWriteOptions; +using ROOT::Experimental::RNTupleWriter; + +#include + +#include +#include +#include +#include +#include + +using Map = std::map>; + +static std::shared_ptr MakeMapField(RNTupleModel &model, + std::string_view name, + EColumnType indexType) { + auto field = std::make_unique>(name); + field->SetColumnRepresentatives({{indexType}}); + field->GetSubFields()[0]->GetSubFields()[1]->SetColumnRepresentatives( + {{indexType}}); + model.AddField(std::move(field)); + return model.GetDefaultEntry().GetPtr(name); +} + +void write(std::string_view filename = "types.map.nested.root") { + if (gSystem->Load("libNestedMap") == -1) + throw std::runtime_error("could not find the required ROOT dictionaries, " + "please make sure to run `make` first"); + + auto model = RNTupleModel::Create(); + + // Non-split index encoding + auto Index32 = MakeMapField(*model, "Index32", EColumnType::kIndex32); + auto Index64 = MakeMapField(*model, "Index64", EColumnType::kIndex64); + + // Split index encoding + auto SplitIndex32 = + MakeMapField(*model, "SplitIndex32", EColumnType::kSplitIndex32); + auto SplitIndex64 = + MakeMapField(*model, "SplitIndex64", EColumnType::kSplitIndex64); + + RNTupleWriteOptions options; + options.SetCompression(0); + auto writer = + RNTupleWriter::Recreate(std::move(model), "ntpl", filename, options); + + // First entry: single-element maps, with ascending values + *Index32 = {{"a", {{"aa", 1}}}}; + *Index64 = {{"b", {{"bb", 2}}}}; + *SplitIndex32 = {{"c", {{"cc", 3}}}}; + *SplitIndex64 = {{"d", {{"dd", 4}}}}; + writer->Fill(); + + // Second entry: empty maps + *Index32 = {}; + *Index64 = {}; + *SplitIndex32 = {}; + *SplitIndex64 = {}; + writer->Fill(); + + // Third entry: increasing number of elements in the outer map + *Index32 = {{"a", {{"aa", 1}}}}; + *Index64 = {{"b", {{"ba", 2}}}, {"c", {{"ca", 3}}}}; + *SplitIndex32 = { + {"d", {{"da", 4}}}, {"e", {{"ea", 5}, {"eb", 6}}}, {"f", {}}}; + *SplitIndex64 = {{"g", {{"ga", 7}, {"gb", 8}}}, + {"h", {}}, + {"i", {{"ia", 9}}}, + {"j", {{"ja", 10}, {"jb", 11}, {"jc", 12}}}}; + writer->Fill(); +} diff --git a/types/multimap/README.md b/types/multimap/README.md new file mode 100644 index 0000000..f6878c4 --- /dev/null +++ b/types/multimap/README.md @@ -0,0 +1,4 @@ +# `std::multimap` + +- [`fundamental`](fundamental): `std::multimap` +- [`nested`](nested): `std::multimap>` diff --git a/types/multimap/fundamental/README.md b/types/multimap/fundamental/README.md new file mode 100644 index 0000000..081bff2 --- /dev/null +++ b/types/multimap/fundamental/README.md @@ -0,0 +1,15 @@ +# `std::map` + +## Fields + +- `[Split]Index{32,64}` + +with the corresponding column type for the offset column of the collection parent field. +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`). + +## Entries + +1. Single-element maps, with ascending values for both key and value +2. Empty maps +3. Increasing number of elements in the map: + one key-value pair in the first field, two key-value pairs in the second field, etc. diff --git a/types/multimap/fundamental/read.C b/types/multimap/fundamental/read.C new file mode 100644 index 0000000..441940c --- /dev/null +++ b/types/multimap/fundamental/read.C @@ -0,0 +1,68 @@ +#include +#include + +using ROOT::Experimental::REntry; +using ROOT::Experimental::RNTupleReader; + +#include +#include +#include +#include +#include +#include + +using Multimap = std::multimap; + +static void PrintMultimapValue(const REntry &entry, std::string_view name, + std::ostream &os, bool last = false) { + Multimap &item = *entry.GetPtr(name); + os << " \"" << name << "\": {"; + bool first = true; + for (auto &[key, value] : item) { + if (first) { + first = false; + } else { + os << ","; + } + os << "\n \"" << key << "\": " << value; + } + if (!item.empty()) { + os << "\n "; + } + os << "}"; + if (!last) { + os << ","; + } + os << "\n"; +} + +void read(std::string_view input = "types.multimap.fundamental.root", + std::string_view output = "types.multimap.fundamental.json") { + std::ofstream os(std::string{output}); + os << "[\n"; + + auto reader = RNTupleReader::Open("ntpl", input); + auto &entry = reader->GetModel().GetDefaultEntry(); + bool first = true; + for (auto index : *reader) { + reader->LoadEntry(index); + + if (first) { + first = false; + } else { + os << ",\n"; + } + os << " {\n"; + + PrintMultimapValue(entry, "Index32", os); + PrintMultimapValue(entry, "Index64", os); + PrintMultimapValue(entry, "SplitIndex32", os); + PrintMultimapValue(entry, "SplitIndex64", os, /*last=*/true); + + os << " }"; + // Newline is intentionally missing, may need to print a comma before the + // next entry. + } + os << "\n"; + os << "]\n"; +} diff --git a/types/multimap/fundamental/write.C b/types/multimap/fundamental/write.C new file mode 100644 index 0000000..3ace02e --- /dev/null +++ b/types/multimap/fundamental/write.C @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include + +using ROOT::Experimental::EColumnType; +using ROOT::Experimental::RField; +using ROOT::Experimental::RNTupleModel; +using ROOT::Experimental::RNTupleWriteOptions; +using ROOT::Experimental::RNTupleWriter; + +#include +#include +#include +#include + +using Multimap = std::multimap; + +static std::shared_ptr MakeMultimapField(RNTupleModel &model, + std::string_view name, + EColumnType indexType) { + auto field = std::make_unique>(name); + field->SetColumnRepresentatives({{indexType}}); + model.AddField(std::move(field)); + return model.GetDefaultEntry().GetPtr(name); +} + +void write(std::string_view filename = "types.multimap.fundamental.root") { + auto model = RNTupleModel::Create(); + + // Non-split index encoding + auto Index32 = MakeMultimapField(*model, "Index32", EColumnType::kIndex32); + auto Index64 = MakeMultimapField(*model, "Index64", EColumnType::kIndex64); + + // Split index encoding + auto SplitIndex32 = + MakeMultimapField(*model, "SplitIndex32", EColumnType::kSplitIndex32); + auto SplitIndex64 = + MakeMultimapField(*model, "SplitIndex64", EColumnType::kSplitIndex64); + + RNTupleWriteOptions options; + options.SetCompression(0); + auto writer = + RNTupleWriter::Recreate(std::move(model), "ntpl", filename, options); + + // First entry: single-element maps, with ascending values + *Index32 = {{"a", 1}}; + *Index64 = {{"b", 2}}; + *SplitIndex32 = {{"c", 3}}; + *SplitIndex64 = {{"d", 4}}; + writer->Fill(); + + // Second entry: empty maps + *Index32 = {}; + *Index64 = {}; + *SplitIndex32 = {}; + *SplitIndex64 = {}; + writer->Fill(); + + // Third entry: increasing number of elements in the map + *Index32 = {{"a", 1}}; + *Index64 = {{"b", 2}, {"c", 3}}; + *SplitIndex32 = {{"d", 4}, {"e", 5}, {"f", 6}}; + *SplitIndex64 = {{"g", 7}, {"h", 8}, {"i", 9}, {"j", 10}}; + writer->Fill(); +} diff --git a/types/multimap/nested/LinkDef.h b/types/multimap/nested/LinkDef.h new file mode 100644 index 0000000..29c1f0f --- /dev/null +++ b/types/multimap/nested/LinkDef.h @@ -0,0 +1,6 @@ +#include +#include + +#ifdef __CLING__ +#pragma link C++ class std::multimap>+; +#endif diff --git a/types/multimap/nested/Makefile b/types/multimap/nested/Makefile new file mode 100644 index 0000000..38ecaa5 --- /dev/null +++ b/types/multimap/nested/Makefile @@ -0,0 +1,26 @@ +ROOT_CONFIG ?= $(shell which root-config) +ifeq ($(ROOT_CONFIG),) +$(error Could not find root-config) +endif +ROOTCLING ?= $(shell which rootcling) +ifeq ($(ROOTCLING),) +$(error Could not find rootcling) +endif + +CXX := $(shell $(ROOT_CONFIG) --cxx) +CXXFLAGS_ROOT := $(shell $(ROOT_CONFIG) --cflags) +CXXFLAGS := -Wall $(CXXFLAGS_ROOT) +LDFLAGS := $(shell $(ROOT_CONFIG) --libs) + +.PHONY: all clean + +all: libNestedMultimap.so + +NestedMultimap.cxx: NestedMultimap.hxx LinkDef.h + $(ROOTCLING) -f $@ $^ + +libNestedMultimap.so: NestedMultimap.cxx + $(CXX) -shared -fPIC -o $@ $^ $(CXXFLAGS) $(LDFLAGS) + +clean: + $(RM) NestedMultimap.cxx NestedMultimap_rdict.pcm libNestedMultimap.so diff --git a/types/multimap/nested/NestedMultimap.hxx b/types/multimap/nested/NestedMultimap.hxx new file mode 100644 index 0000000..cf43459 --- /dev/null +++ b/types/multimap/nested/NestedMultimap.hxx @@ -0,0 +1,4 @@ +#pragma once + +#include +#include diff --git a/types/multimap/nested/README.md b/types/multimap/nested/README.md new file mode 100644 index 0000000..889c357 --- /dev/null +++ b/types/multimap/nested/README.md @@ -0,0 +1,18 @@ +# `std::multimap>` + +## Fields + +- `[Split]Index{32,64}` + +with the corresponding column type for the offset column of the two collection parent fields. +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`). + +## Entries + +1. Single-element maps, with ascending values for both key and value +2. Empty maps +3. Increasing number of elements in the outer map, with arbitrary lengths of the inner maps + +## Dictionaries + +These tests require ROOT dictionaries, which can be generated with the provided `Makefile` in this directory. This will create a `libNestedMultimap` shared object. diff --git a/types/multimap/nested/read.C b/types/multimap/nested/read.C new file mode 100644 index 0000000..74c4580 --- /dev/null +++ b/types/multimap/nested/read.C @@ -0,0 +1,89 @@ +#include +#include + +using ROOT::Experimental::REntry; +using ROOT::Experimental::RNTupleReader; + +#include + +#include +#include +#include +#include +#include +#include +#include + +using Multimap = + std::multimap>; + +static void PrintNestedMultimapValue(const REntry &entry, std::string_view name, + std::ostream &os, bool last = false) { + Multimap &item = *entry.GetPtr(name); + os << " \"" << name << "\": {"; + bool outerFirst = true; + for (auto &[key, inner] : item) { + if (outerFirst) { + outerFirst = false; + } else { + os << ","; + } + os << "\n \"" << key << "\": {"; + bool innerFirst = true; + for (auto &[innerKey, value] : inner) { + if (innerFirst) { + innerFirst = false; + } else { + os << ","; + } + os << "\n \"" << innerKey << "\": " << value; + } + if (!inner.empty()) { + os << "\n "; + } + os << "}"; + } + if (!item.empty()) { + os << "\n "; + } + os << "}"; + if (!last) { + os << ","; + } + os << "\n"; +} + +void read(std::string_view input = "types.multimap.nested.root", + std::string_view output = "types.multimap.nested.json") { + if (gSystem->Load("libNestedMultimap") == -1) + throw std::runtime_error("could not find the required ROOT dictionaries, " + "please make sure to run `make` first"); + + std::ofstream os(std::string{output}); + os << "[\n"; + + auto reader = RNTupleReader::Open("ntpl", input); + auto &entry = reader->GetModel().GetDefaultEntry(); + bool first = true; + for (auto index : *reader) { + reader->LoadEntry(index); + + if (first) { + first = false; + } else { + os << ",\n"; + } + os << " {\n"; + + PrintNestedMultimapValue(entry, "Index32", os); + PrintNestedMultimapValue(entry, "Index64", os); + PrintNestedMultimapValue(entry, "SplitIndex32", os); + PrintNestedMultimapValue(entry, "SplitIndex64", os, /*last=*/true); + + os << " }"; + // Newline is intentionally missing, may need to print a comma before the + // next entry. + } + os << "\n"; + os << "]\n"; +} diff --git a/types/multimap/nested/write.C b/types/multimap/nested/write.C new file mode 100644 index 0000000..144dabb --- /dev/null +++ b/types/multimap/nested/write.C @@ -0,0 +1,81 @@ +#include +#include +#include +#include +#include + +using ROOT::Experimental::EColumnType; +using ROOT::Experimental::RField; +using ROOT::Experimental::RNTupleModel; +using ROOT::Experimental::RNTupleWriteOptions; +using ROOT::Experimental::RNTupleWriter; + +#include + +#include +#include +#include +#include +#include + +using Multimap = + std::multimap>; + +static std::shared_ptr MakeMultimapField(RNTupleModel &model, + std::string_view name, + EColumnType indexType) { + auto field = std::make_unique>(name); + field->SetColumnRepresentatives({{indexType}}); + field->GetSubFields()[0]->GetSubFields()[1]->SetColumnRepresentatives( + {{indexType}}); + model.AddField(std::move(field)); + return model.GetDefaultEntry().GetPtr(name); +} + +void write(std::string_view filename = "types.multimap.nested.root") { + if (gSystem->Load("libNestedMultimap") == -1) + throw std::runtime_error("could not find the required ROOT dictionaries, " + "please make sure to run `make` first"); + + auto model = RNTupleModel::Create(); + + // Non-split index encoding + auto Index32 = MakeMultimapField(*model, "Index32", EColumnType::kIndex32); + auto Index64 = MakeMultimapField(*model, "Index64", EColumnType::kIndex64); + + // Split index encoding + auto SplitIndex32 = + MakeMultimapField(*model, "SplitIndex32", EColumnType::kSplitIndex32); + auto SplitIndex64 = + MakeMultimapField(*model, "SplitIndex64", EColumnType::kSplitIndex64); + + RNTupleWriteOptions options; + options.SetCompression(0); + auto writer = + RNTupleWriter::Recreate(std::move(model), "ntpl", filename, options); + + // First entry: single-element maps, with ascending values + *Index32 = {{"a", {{"aa", 1}}}}; + *Index64 = {{"b", {{"bb", 2}}}}; + *SplitIndex32 = {{"c", {{"cc", 3}}}}; + *SplitIndex64 = {{"d", {{"dd", 4}}}}; + writer->Fill(); + + // Second entry: empty maps + *Index32 = {}; + *Index64 = {}; + *SplitIndex32 = {}; + *SplitIndex64 = {}; + writer->Fill(); + + // Third entry: increasing number of elements in the outer map + *Index32 = {{"a", {{"aa", 1}}}}; + *Index64 = {{"b", {{"ba", 2}}}, {"c", {{"ca", 3}}}}; + *SplitIndex32 = { + {"d", {{"da", 4}}}, {"e", {{"ea", 5}, {"eb", 6}}}, {"f", {}}}; + *SplitIndex64 = {{"g", {{"ga", 7}, {"gb", 8}}}, + {"h", {}}, + {"i", {{"ia", 9}}}, + {"j", {{"ja", 10}, {"jb", 11}, {"jc", 12}}}}; + writer->Fill(); +} diff --git a/types/unordered_map/README.md b/types/unordered_map/README.md new file mode 100644 index 0000000..753c3eb --- /dev/null +++ b/types/unordered_map/README.md @@ -0,0 +1,4 @@ +# `std::unordered_map` + +- [`fundamental`](fundamental): `std::unordered_map` +- [`nested`](nested): `std::unordered_map>` diff --git a/types/unordered_map/fundamental/README.md b/types/unordered_map/fundamental/README.md new file mode 100644 index 0000000..539ce74 --- /dev/null +++ b/types/unordered_map/fundamental/README.md @@ -0,0 +1,15 @@ +# `std::unordered_map` + +## Fields + +- `[Split]Index{32,64}` + +with the corresponding column type for the offset column of the collection parent field. +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`). + +## Entries + +1. Single-element maps, with ascending values for both key and value +2. Empty maps +3. Increasing number of elements in the map: + one key-value pair in the first field, two key-value pairs in the second field, etc. diff --git a/types/unordered_map/fundamental/read.C b/types/unordered_map/fundamental/read.C new file mode 100644 index 0000000..be38ff2 --- /dev/null +++ b/types/unordered_map/fundamental/read.C @@ -0,0 +1,68 @@ +#include +#include + +using ROOT::Experimental::REntry; +using ROOT::Experimental::RNTupleReader; + +#include +#include +#include +#include +#include +#include + +using UnorderedMap = std::unordered_map; + +static void PrintMapValue(const REntry &entry, std::string_view name, + std::ostream &os, bool last = false) { + UnorderedMap &item = *entry.GetPtr(name); + os << " \"" << name << "\": {"; + bool first = true; + for (auto &[key, value] : item) { + if (first) { + first = false; + } else { + os << ","; + } + os << "\n \"" << key << "\": " << value; + } + if (!item.empty()) { + os << "\n "; + } + os << "}"; + if (!last) { + os << ","; + } + os << "\n"; +} + +void read(std::string_view input = "types.unordered_map.fundamental.root", + std::string_view output = "types.unordered_map.fundamental.json") { + std::ofstream os(std::string{output}); + os << "[\n"; + + auto reader = RNTupleReader::Open("ntpl", input); + auto &entry = reader->GetModel().GetDefaultEntry(); + bool first = true; + for (auto index : *reader) { + reader->LoadEntry(index); + + if (first) { + first = false; + } else { + os << ",\n"; + } + os << " {\n"; + + PrintMapValue(entry, "Index32", os); + PrintMapValue(entry, "Index64", os); + PrintMapValue(entry, "SplitIndex32", os); + PrintMapValue(entry, "SplitIndex64", os, /*last=*/true); + + os << " }"; + // Newline is intentionally missing, may need to print a comma before the + // next entry. + } + os << "\n"; + os << "]\n"; +} diff --git a/types/unordered_map/fundamental/write.C b/types/unordered_map/fundamental/write.C new file mode 100644 index 0000000..58f102a --- /dev/null +++ b/types/unordered_map/fundamental/write.C @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include + +using ROOT::Experimental::EColumnType; +using ROOT::Experimental::RField; +using ROOT::Experimental::RNTupleModel; +using ROOT::Experimental::RNTupleWriteOptions; +using ROOT::Experimental::RNTupleWriter; + +#include +#include +#include +#include + +using UnorderedMap = std::unordered_map; + +static std::shared_ptr MakeMapField(RNTupleModel &model, + std::string_view name, + EColumnType indexType) { + auto field = std::make_unique>(name); + field->SetColumnRepresentatives({{indexType}}); + model.AddField(std::move(field)); + return model.GetDefaultEntry().GetPtr(name); +} + +void write(std::string_view filename = "types.unordered_map.fundamental.root") { + auto model = RNTupleModel::Create(); + + // Non-split index encoding + auto Index32 = MakeMapField(*model, "Index32", EColumnType::kIndex32); + auto Index64 = MakeMapField(*model, "Index64", EColumnType::kIndex64); + + // Split index encoding + auto SplitIndex32 = + MakeMapField(*model, "SplitIndex32", EColumnType::kSplitIndex32); + auto SplitIndex64 = + MakeMapField(*model, "SplitIndex64", EColumnType::kSplitIndex64); + + RNTupleWriteOptions options; + options.SetCompression(0); + auto writer = + RNTupleWriter::Recreate(std::move(model), "ntpl", filename, options); + + // First entry: single-element maps, with ascending values + *Index32 = {{"a", 1}}; + *Index64 = {{"b", 2}}; + *SplitIndex32 = {{"c", 3}}; + *SplitIndex64 = {{"d", 4}}; + writer->Fill(); + + // Second entry: empty maps + *Index32 = {}; + *Index64 = {}; + *SplitIndex32 = {}; + *SplitIndex64 = {}; + writer->Fill(); + + // Third entry: increasing number of elements in the map + *Index32 = {{"a", 1}}; + *Index64 = {{"b", 2}, {"c", 3}}; + *SplitIndex32 = {{"d", 4}, {"e", 5}, {"f", 6}}; + *SplitIndex64 = {{"g", 7}, {"h", 8}, {"i", 9}, {"j", 10}}; + writer->Fill(); +} diff --git a/types/unordered_map/nested/LinkDef.h b/types/unordered_map/nested/LinkDef.h new file mode 100644 index 0000000..e788d1e --- /dev/null +++ b/types/unordered_map/nested/LinkDef.h @@ -0,0 +1,6 @@ +#include +#include + +#ifdef __CLING__ +#pragma link C++ class std::unordered_map>+; +#endif diff --git a/types/unordered_map/nested/Makefile b/types/unordered_map/nested/Makefile new file mode 100644 index 0000000..0be969d --- /dev/null +++ b/types/unordered_map/nested/Makefile @@ -0,0 +1,26 @@ +ROOT_CONFIG ?= $(shell which root-config) +ifeq ($(ROOT_CONFIG),) +$(error Could not find root-config) +endif +ROOTCLING ?= $(shell which rootcling) +ifeq ($(ROOTCLING),) +$(error Could not find rootcling) +endif + +CXX := $(shell $(ROOT_CONFIG) --cxx) +CXXFLAGS_ROOT := $(shell $(ROOT_CONFIG) --cflags) +CXXFLAGS := -Wall $(CXXFLAGS_ROOT) +LDFLAGS := $(shell $(ROOT_CONFIG) --libs) + +.PHONY: all clean + +all: libNestedUnorderedMap.so + +NestedUnorderedMap.cxx: NestedUnorderedMap.hxx LinkDef.h + $(ROOTCLING) -f $@ $^ + +libNestedUnorderedMap.so: NestedUnorderedMap.cxx + $(CXX) -shared -fPIC -o $@ $^ $(CXXFLAGS) $(LDFLAGS) + +clean: + $(RM) NestedUnorderedMap.cxx NestedUnorderedMap_rdict.pcm libNestedUnorderedMap.so diff --git a/types/unordered_map/nested/NestedUnorderedMap.hxx b/types/unordered_map/nested/NestedUnorderedMap.hxx new file mode 100644 index 0000000..982dfac --- /dev/null +++ b/types/unordered_map/nested/NestedUnorderedMap.hxx @@ -0,0 +1,4 @@ +#pragma once + +#include +#include diff --git a/types/unordered_map/nested/README.md b/types/unordered_map/nested/README.md new file mode 100644 index 0000000..8b08635 --- /dev/null +++ b/types/unordered_map/nested/README.md @@ -0,0 +1,18 @@ +# `std::unordered_map>` + +## Fields + +- `[Split]Index{32,64}` + +with the corresponding column type for the offset column of the two collection parent fields. +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`). + +## Entries + +1. Single-element maps, with ascending values for both key and value +2. Empty maps +3. Increasing number of elements in the outer map, with arbitrary lengths of the inner maps + +## Dictionaries + +These tests require ROOT dictionaries, which can be generated with the provided `Makefile` in this directory. This will create a `libNestedUnorderedMap` shared object. diff --git a/types/unordered_map/nested/read.C b/types/unordered_map/nested/read.C new file mode 100644 index 0000000..6c2d2d5 --- /dev/null +++ b/types/unordered_map/nested/read.C @@ -0,0 +1,91 @@ +#include +#include + +using ROOT::Experimental::REntry; +using ROOT::Experimental::RNTupleReader; + +#include + +#include +#include +#include +#include +#include +#include +#include + +using UnorderedMap = + std::unordered_map>; + +static void PrintNestedUnorderedMapValue(const REntry &entry, + std::string_view name, + std::ostream &os, bool last = false) { + UnorderedMap &item = *entry.GetPtr(name); + os << " \"" << name << "\": {"; + bool outerFirst = true; + for (auto &[key, inner] : item) { + if (outerFirst) { + outerFirst = false; + } else { + os << ","; + } + os << "\n \"" << key << "\": {"; + bool innerFirst = true; + for (auto &[innerKey, value] : inner) { + if (innerFirst) { + innerFirst = false; + } else { + os << ","; + } + os << "\n \"" << innerKey << "\": " << value; + } + if (!inner.empty()) { + os << "\n "; + } + os << "}"; + } + if (!item.empty()) { + os << "\n "; + } + os << "}"; + if (!last) { + os << ","; + } + os << "\n"; +} + +void read(std::string_view input = "types.unordered_map.nested.root", + std::string_view output = "types.unordered_map.nested.json") { + if (gSystem->Load("libNestedUnorderedMap") == -1) + throw std::runtime_error("could not find the required ROOT dictionaries, " + "please make sure to run `make` first"); + + std::ofstream os(std::string{output}); + os << "[\n"; + + auto reader = RNTupleReader::Open("ntpl", input); + auto &entry = reader->GetModel().GetDefaultEntry(); + bool first = true; + for (auto index : *reader) { + reader->LoadEntry(index); + + if (first) { + first = false; + } else { + os << ",\n"; + } + os << " {\n"; + + PrintNestedUnorderedMapValue(entry, "Index32", os); + PrintNestedUnorderedMapValue(entry, "Index64", os); + PrintNestedUnorderedMapValue(entry, "SplitIndex32", os); + PrintNestedUnorderedMapValue(entry, "SplitIndex64", os, /*last=*/true); + + os << " }"; + // Newline is intentionally missing, may need to print a comma before the + // next entry. + } + os << "\n"; + os << "]\n"; +} diff --git a/types/unordered_map/nested/write.C b/types/unordered_map/nested/write.C new file mode 100644 index 0000000..2e53014 --- /dev/null +++ b/types/unordered_map/nested/write.C @@ -0,0 +1,81 @@ +#include +#include +#include +#include +#include + +using ROOT::Experimental::EColumnType; +using ROOT::Experimental::RField; +using ROOT::Experimental::RNTupleModel; +using ROOT::Experimental::RNTupleWriteOptions; +using ROOT::Experimental::RNTupleWriter; + +#include + +#include +#include +#include +#include +#include + +using UnorderedMap = std::unordered_map>; + +static std::shared_ptr MakeUnorderedMapField(RNTupleModel &model, + std::string_view name, + EColumnType indexType) { + auto field = std::make_unique>(name); + field->SetColumnRepresentatives({{indexType}}); + field->GetSubFields()[0]->GetSubFields()[1]->SetColumnRepresentatives( + {{indexType}}); + model.AddField(std::move(field)); + return model.GetDefaultEntry().GetPtr(name); +} + +void write(std::string_view filename = "types.unordered_map.nested.root") { + if (gSystem->Load("libNestedUnorderedMap") == -1) + throw std::runtime_error("could not find the required ROOT dictionaries, " + "please make sure to run `make` first"); + + auto model = RNTupleModel::Create(); + + // Non-split index encoding + auto Index32 = MakeUnorderedMapField(*model, "Index32", EColumnType::kIndex32); + auto Index64 = MakeUnorderedMapField(*model, "Index64", EColumnType::kIndex64); + + // Split index encoding + auto SplitIndex32 = + MakeUnorderedMapField(*model, "SplitIndex32", EColumnType::kSplitIndex32); + auto SplitIndex64 = + MakeUnorderedMapField(*model, "SplitIndex64", EColumnType::kSplitIndex64); + + RNTupleWriteOptions options; + options.SetCompression(0); + auto writer = + RNTupleWriter::Recreate(std::move(model), "ntpl", filename, options); + + // First entry: single-element maps, with ascending values + *Index32 = {{"a", {{"aa", 1}}}}; + *Index64 = {{"b", {{"bb", 2}}}}; + *SplitIndex32 = {{"c", {{"cc", 3}}}}; + *SplitIndex64 = {{"d", {{"dd", 4}}}}; + writer->Fill(); + + // Second entry: empty maps + *Index32 = {}; + *Index64 = {}; + *SplitIndex32 = {}; + *SplitIndex64 = {}; + writer->Fill(); + + // Third entry: increasing number of elements in the outer map + *Index32 = {{"a", {{"aa", 1}}}}; + *Index64 = {{"b", {{"ba", 2}}}, {"c", {{"ca", 3}}}}; + *SplitIndex32 = { + {"d", {{"da", 4}}}, {"e", {{"ea", 5}, {"eb", 6}}}, {"f", {}}}; + *SplitIndex64 = {{"g", {{"ga", 7}, {"gb", 8}}}, + {"h", {}}, + {"i", {{"ia", 9}}}, + {"j", {{"ja", 10}, {"jb", 11}, {"jc", 12}}}}; + writer->Fill(); +} diff --git a/types/unordered_multimap/README.md b/types/unordered_multimap/README.md new file mode 100644 index 0000000..753c3eb --- /dev/null +++ b/types/unordered_multimap/README.md @@ -0,0 +1,4 @@ +# `std::unordered_map` + +- [`fundamental`](fundamental): `std::unordered_map` +- [`nested`](nested): `std::unordered_map>` diff --git a/types/unordered_multimap/fundamental/README.md b/types/unordered_multimap/fundamental/README.md new file mode 100644 index 0000000..42a4fce --- /dev/null +++ b/types/unordered_multimap/fundamental/README.md @@ -0,0 +1,15 @@ +# `std::unordered_multimap` + +## Fields + +- `[Split]Index{32,64}` + +with the corresponding column type for the offset column of the collection parent field. +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`). + +## Entries + +1. Single-element maps, with ascending values for both key and value +2. Empty maps +3. Increasing number of elements in the map: + one key-value pair in the first field, two key-value pairs in the second field, etc. diff --git a/types/unordered_multimap/fundamental/read.C b/types/unordered_multimap/fundamental/read.C new file mode 100644 index 0000000..346d328 --- /dev/null +++ b/types/unordered_multimap/fundamental/read.C @@ -0,0 +1,68 @@ +#include +#include + +using ROOT::Experimental::REntry; +using ROOT::Experimental::RNTupleReader; + +#include +#include +#include +#include +#include +#include + +using UnorderedMultimap = std::unordered_multimap; + +static void PrintMultimapValue(const REntry &entry, std::string_view name, + std::ostream &os, bool last = false) { + UnorderedMultimap &item = *entry.GetPtr(name); + os << " \"" << name << "\": {"; + bool first = true; + for (auto &[key, value] : item) { + if (first) { + first = false; + } else { + os << ","; + } + os << "\n \"" << key << "\": " << value; + } + if (!item.empty()) { + os << "\n "; + } + os << "}"; + if (!last) { + os << ","; + } + os << "\n"; +} + +void read(std::string_view input = "types.unordered_multimap.fundamental.root", + std::string_view output = "types.unordered_multimap.fundamental.json") { + std::ofstream os(std::string{output}); + os << "[\n"; + + auto reader = RNTupleReader::Open("ntpl", input); + auto &entry = reader->GetModel().GetDefaultEntry(); + bool first = true; + for (auto index : *reader) { + reader->LoadEntry(index); + + if (first) { + first = false; + } else { + os << ",\n"; + } + os << " {\n"; + + PrintMultimapValue(entry, "Index32", os); + PrintMultimapValue(entry, "Index64", os); + PrintMultimapValue(entry, "SplitIndex32", os); + PrintMultimapValue(entry, "SplitIndex64", os, /*last=*/true); + + os << " }"; + // Newline is intentionally missing, may need to print a comma before the + // next entry. + } + os << "\n"; + os << "]\n"; +} diff --git a/types/unordered_multimap/fundamental/write.C b/types/unordered_multimap/fundamental/write.C new file mode 100644 index 0000000..bdebb90 --- /dev/null +++ b/types/unordered_multimap/fundamental/write.C @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include + +using ROOT::Experimental::EColumnType; +using ROOT::Experimental::RField; +using ROOT::Experimental::RNTupleModel; +using ROOT::Experimental::RNTupleWriteOptions; +using ROOT::Experimental::RNTupleWriter; + +#include +#include +#include +#include + +using UnorderedMultimap = std::unordered_multimap; + +static std::shared_ptr MakeMultimapField(RNTupleModel &model, + std::string_view name, + EColumnType indexType) { + auto field = std::make_unique>(name); + field->SetColumnRepresentatives({{indexType}}); + model.AddField(std::move(field)); + return model.GetDefaultEntry().GetPtr(name); +} + +void write(std::string_view filename = "types.unordered_multimap.fundamental.root") { + auto model = RNTupleModel::Create(); + + // Non-split index encoding + auto Index32 = MakeMultimapField(*model, "Index32", EColumnType::kIndex32); + auto Index64 = MakeMultimapField(*model, "Index64", EColumnType::kIndex64); + + // Split index encoding + auto SplitIndex32 = + MakeMultimapField(*model, "SplitIndex32", EColumnType::kSplitIndex32); + auto SplitIndex64 = + MakeMultimapField(*model, "SplitIndex64", EColumnType::kSplitIndex64); + + RNTupleWriteOptions options; + options.SetCompression(0); + auto writer = + RNTupleWriter::Recreate(std::move(model), "ntpl", filename, options); + + // First entry: single-element maps, with ascending values + *Index32 = {{"a", 1}}; + *Index64 = {{"b", 2}}; + *SplitIndex32 = {{"c", 3}}; + *SplitIndex64 = {{"d", 4}}; + writer->Fill(); + + // Second entry: empty maps + *Index32 = {}; + *Index64 = {}; + *SplitIndex32 = {}; + *SplitIndex64 = {}; + writer->Fill(); + + // Third entry: increasing number of elements in the map + *Index32 = {{"a", 1}}; + *Index64 = {{"b", 2}, {"c", 3}}; + *SplitIndex32 = {{"d", 4}, {"e", 5}, {"f", 6}}; + *SplitIndex64 = {{"g", 7}, {"h", 8}, {"i", 9}, {"j", 10}}; + writer->Fill(); +} diff --git a/types/unordered_multimap/nested/LinkDef.h b/types/unordered_multimap/nested/LinkDef.h new file mode 100644 index 0000000..b9608d9 --- /dev/null +++ b/types/unordered_multimap/nested/LinkDef.h @@ -0,0 +1,6 @@ +#include +#include + +#ifdef __CLING__ +#pragma link C++ class std::unordered_multimap>+; +#endif diff --git a/types/unordered_multimap/nested/Makefile b/types/unordered_multimap/nested/Makefile new file mode 100644 index 0000000..268e35a --- /dev/null +++ b/types/unordered_multimap/nested/Makefile @@ -0,0 +1,26 @@ +ROOT_CONFIG ?= $(shell which root-config) +ifeq ($(ROOT_CONFIG),) +$(error Could not find root-config) +endif +ROOTCLING ?= $(shell which rootcling) +ifeq ($(ROOTCLING),) +$(error Could not find rootcling) +endif + +CXX := $(shell $(ROOT_CONFIG) --cxx) +CXXFLAGS_ROOT := $(shell $(ROOT_CONFIG) --cflags) +CXXFLAGS := -Wall $(CXXFLAGS_ROOT) +LDFLAGS := $(shell $(ROOT_CONFIG) --libs) + +.PHONY: all clean + +all: libNestedUnorderedMultimap.so + +NestedUnorderedMultimap.cxx: NestedUnorderedMultimap.hxx LinkDef.h + $(ROOTCLING) -f $@ $^ + +libNestedUnorderedMultimap.so: NestedUnorderedMultimap.cxx + $(CXX) -shared -fPIC -o $@ $^ $(CXXFLAGS) $(LDFLAGS) + +clean: + $(RM) NestedUnorderedMultimap.cxx NestedUnorderedMultimap_rdict.pcm libNestedUnorderedMultimap.so diff --git a/types/unordered_multimap/nested/NestedUnorderedMultimap.hxx b/types/unordered_multimap/nested/NestedUnorderedMultimap.hxx new file mode 100644 index 0000000..982dfac --- /dev/null +++ b/types/unordered_multimap/nested/NestedUnorderedMultimap.hxx @@ -0,0 +1,4 @@ +#pragma once + +#include +#include diff --git a/types/unordered_multimap/nested/README.md b/types/unordered_multimap/nested/README.md new file mode 100644 index 0000000..8f941e3 --- /dev/null +++ b/types/unordered_multimap/nested/README.md @@ -0,0 +1,18 @@ +# `std::unordered_multimap>` + +## Fields + +- `[Split]Index{32,64}` + +with the corresponding column type for the offset column of the two collection parent fields. +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`). + +## Entries + +1. Single-element maps, with ascending values for both key and value +2. Empty maps +3. Increasing number of elements in the outer map, with arbitrary lengths of the inner maps + +## Dictionaries + +These tests require ROOT dictionaries, which can be generated with the provided `Makefile` in this directory. This will create a `libNestedUnorderedMultimap` shared object. diff --git a/types/unordered_multimap/nested/read.C b/types/unordered_multimap/nested/read.C new file mode 100644 index 0000000..77f375d --- /dev/null +++ b/types/unordered_multimap/nested/read.C @@ -0,0 +1,91 @@ +#include +#include + +using ROOT::Experimental::REntry; +using ROOT::Experimental::RNTupleReader; + +#include + +#include +#include +#include +#include +#include +#include +#include + +using UnorderedMultimap = + std::unordered_multimap>; + +static void PrintNestedUnorderedMultimapValue(const REntry &entry, + std::string_view name, + std::ostream &os, bool last = false) { + UnorderedMultimap &item = *entry.GetPtr(name); + os << " \"" << name << "\": {"; + bool outerFirst = true; + for (auto &[key, inner] : item) { + if (outerFirst) { + outerFirst = false; + } else { + os << ","; + } + os << "\n \"" << key << "\": {"; + bool innerFirst = true; + for (auto &[innerKey, value] : inner) { + if (innerFirst) { + innerFirst = false; + } else { + os << ","; + } + os << "\n \"" << innerKey << "\": " << value; + } + if (!inner.empty()) { + os << "\n "; + } + os << "}"; + } + if (!item.empty()) { + os << "\n "; + } + os << "}"; + if (!last) { + os << ","; + } + os << "\n"; +} + +void read(std::string_view input = "types.unordered_multimap.nested.root", + std::string_view output = "types.unordered_multimap.nested.json") { + if (gSystem->Load("libNestedUnorderedMultimap") == -1) + throw std::runtime_error("could not find the required ROOT dictionaries, " + "please make sure to run `make` first"); + + std::ofstream os(std::string{output}); + os << "[\n"; + + auto reader = RNTupleReader::Open("ntpl", input); + auto &entry = reader->GetModel().GetDefaultEntry(); + bool first = true; + for (auto index : *reader) { + reader->LoadEntry(index); + + if (first) { + first = false; + } else { + os << ",\n"; + } + os << " {\n"; + + PrintNestedUnorderedMultimapValue(entry, "Index32", os); + PrintNestedUnorderedMultimapValue(entry, "Index64", os); + PrintNestedUnorderedMultimapValue(entry, "SplitIndex32", os); + PrintNestedUnorderedMultimapValue(entry, "SplitIndex64", os, /*last=*/true); + + os << " }"; + // Newline is intentionally missing, may need to print a comma before the + // next entry. + } + os << "\n"; + os << "]\n"; +} diff --git a/types/unordered_multimap/nested/write.C b/types/unordered_multimap/nested/write.C new file mode 100644 index 0000000..74c54b5 --- /dev/null +++ b/types/unordered_multimap/nested/write.C @@ -0,0 +1,81 @@ +#include +#include +#include +#include +#include + +using ROOT::Experimental::EColumnType; +using ROOT::Experimental::RField; +using ROOT::Experimental::RNTupleModel; +using ROOT::Experimental::RNTupleWriteOptions; +using ROOT::Experimental::RNTupleWriter; + +#include + +#include +#include +#include +#include +#include + +using UnorderedMultimap = std::unordered_multimap>; + +static std::shared_ptr MakeUnorderedMultimapField(RNTupleModel &model, + std::string_view name, + EColumnType indexType) { + auto field = std::make_unique>(name); + field->SetColumnRepresentatives({{indexType}}); + field->GetSubFields()[0]->GetSubFields()[1]->SetColumnRepresentatives( + {{indexType}}); + model.AddField(std::move(field)); + return model.GetDefaultEntry().GetPtr(name); +} + +void write(std::string_view filename = "types.unordered_multimap.nested.root") { + if (gSystem->Load("libNestedUnorderedMultimap") == -1) + throw std::runtime_error("could not find the required ROOT dictionaries, " + "please make sure to run `make` first"); + + auto model = RNTupleModel::Create(); + + // Non-split index encoding + auto Index32 = MakeUnorderedMultimapField(*model, "Index32", EColumnType::kIndex32); + auto Index64 = MakeUnorderedMultimapField(*model, "Index64", EColumnType::kIndex64); + + // Split index encoding + auto SplitIndex32 = + MakeUnorderedMultimapField(*model, "SplitIndex32", EColumnType::kSplitIndex32); + auto SplitIndex64 = + MakeUnorderedMultimapField(*model, "SplitIndex64", EColumnType::kSplitIndex64); + + RNTupleWriteOptions options; + options.SetCompression(0); + auto writer = + RNTupleWriter::Recreate(std::move(model), "ntpl", filename, options); + + // First entry: single-element maps, with ascending values + *Index32 = {{"a", {{"aa", 1}}}}; + *Index64 = {{"b", {{"bb", 2}}}}; + *SplitIndex32 = {{"c", {{"cc", 3}}}}; + *SplitIndex64 = {{"d", {{"dd", 4}}}}; + writer->Fill(); + + // Second entry: empty maps + *Index32 = {}; + *Index64 = {}; + *SplitIndex32 = {}; + *SplitIndex64 = {}; + writer->Fill(); + + // Third entry: increasing number of elements in the outer map + *Index32 = {{"a", {{"aa", 1}}}}; + *Index64 = {{"b", {{"ba", 2}}}, {"c", {{"ca", 3}}}}; + *SplitIndex32 = { + {"d", {{"da", 4}}}, {"e", {{"ea", 5}, {"eb", 6}}}, {"f", {}}}; + *SplitIndex64 = {{"g", {{"ga", 7}, {"gb", 8}}}, + {"h", {}}, + {"i", {{"ia", 9}}}, + {"j", {{"ja", 10}, {"jb", 11}, {"jc", 12}}}}; + writer->Fill(); +}