Skip to content

Commit b0332c2

Browse files
committed
Preserve insertion order in G3TimestreamMap
Use a custom OrderedMap class that behaves identically to std::map, except that items are iterated over in the order they were inserted into the map. This allows preserving the detector ordering when creating a G3TimestreamMap from a 2D array and a set of keys, e.g. when converting between G3SuperTimestream and G3TimestreamMap objects, as the former are not guaranteed to be ordered alphabetically.
1 parent 9ffbf19 commit b0332c2

File tree

3 files changed

+187
-11
lines changed

3 files changed

+187
-11
lines changed

core/include/core/G3Timestream.h

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
#include <G3TimeStamp.h>
66
#include <G3Vector.h>
77
#include <vector>
8-
#include <map>
8+
#include <containers.h>
99

1010
class G3Timestream : public G3FrameObject {
1111
public:
@@ -181,11 +181,13 @@ struct G3Timestream::TimeStreamTypeResolver<int64_t>{
181181
};
182182

183183
class G3TimestreamMap : public G3FrameObject,
184-
public std::map<std::string, G3TimestreamPtr> {
184+
public OrderedMap<std::string, G3TimestreamPtr>{
185185
public:
186186
G3TimestreamMap(){}
187-
G3TimestreamMap(const G3TimestreamMap& other):std::map<std::string, G3TimestreamPtr>(other){}
188-
G3TimestreamMap(G3TimestreamMap&& other):std::map<std::string, G3TimestreamPtr>(std::move(other)){}
187+
G3TimestreamMap(const G3TimestreamMap& other) :
188+
OrderedMap<std::string, G3TimestreamPtr>(other) {}
189+
G3TimestreamMap(G3TimestreamMap&& other) :
190+
OrderedMap<std::string, G3TimestreamPtr>(std::move(other)) {}
189191

190192
G3TimestreamMap& operator=(const G3TimestreamMap&)=default;
191193
G3TimestreamMap& operator=(G3TimestreamMap&&)=default;
@@ -253,8 +255,6 @@ class G3TimestreamMap : public G3FrameObject,
253255
void FromBuffer(const std::vector<std::string>& keys, std::size_t n_samples,
254256
std::shared_ptr<SampleType[]> data, G3Time start, G3Time stop,
255257
G3Timestream::TimestreamUnits units=G3Timestream::None, int compression_level=0) {
256-
if(!std::is_sorted(keys.begin(), keys.end()))
257-
throw std::runtime_error("G3TimestreamMap::MakeCompact: keys must be sorted");
258258
const auto data_type=G3Timestream::TimeStreamTypeResolver<SampleType>::type_tag;
259259
std::size_t offset = 0;
260260
for (const auto& key : keys) {
@@ -267,7 +267,7 @@ class G3TimestreamMap : public G3FrameObject,
267267
ts->data_ = data.get() + offset;
268268
ts->data_type_ = data_type;
269269
ts->len_ = n_samples;
270-
emplace(key, std::move(ts));
270+
insert({key, std::move(ts)});
271271
offset += n_samples;
272272
}
273273
}
@@ -284,7 +284,7 @@ namespace cereal {
284284
}
285285

286286
G3_SERIALIZABLE(G3Timestream, 3);
287-
G3_SERIALIZABLE(G3TimestreamMap, 3);
287+
G3_SERIALIZABLE(G3TimestreamMap, 4);
288288

289289
#endif
290290

core/include/core/containers.h

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
#ifndef _G3_CONTAINERS_H
2+
#define _G3_CONTAINERS_H
3+
4+
#include <vector>
5+
#include <unordered_map>
6+
#include <iterator>
7+
8+
template <typename K, typename T>
9+
class OrderedMap {
10+
public:
11+
using map_type = std::unordered_map<K, T>;
12+
using key_map_type = std::vector<K>;
13+
using key_type = K;
14+
using mapped_type = T;
15+
using value_type = typename map_type::value_type;
16+
using difference_type = typename map_type::difference_type;
17+
using size_type = typename map_type::size_type;
18+
19+
OrderedMap() {};
20+
OrderedMap(const OrderedMap& other) :
21+
map_(other.map_), keys_(other.keys_) {}
22+
OrderedMap(OrderedMap&& other) :
23+
map_(std::move(other.map_)), keys_(std::move(other.keys_)) {}
24+
virtual ~OrderedMap() {};
25+
26+
OrderedMap& operator=(const OrderedMap&) = default;
27+
OrderedMap& operator=(OrderedMap&&) = default;
28+
29+
mapped_type& operator[](const key_type& key) {
30+
auto it = std::find(keys_.begin(), keys_.end(), key);
31+
if (it == keys_.end())
32+
keys_.push_back(key);
33+
return map_[key];
34+
}
35+
const mapped_type& operator[](const key_type& key) const { return map_.at(key); }
36+
mapped_type& at(const key_type& key) { return map_.at(key); }
37+
const mapped_type& at(const key_type& key) const { return map_.at(key); }
38+
bool contains(const key_type& key) const { return map_.count(key) > 0; }
39+
40+
size_type size() const { return map_.size(); }
41+
size_type max_size() const { return map_.max_size(); }
42+
bool empty() const { return map_.empty(); }
43+
44+
void clear() {
45+
map_.clear();
46+
keys_.clear();
47+
}
48+
49+
size_type erase(const key_type& key) {
50+
auto it = std::find(keys_.begin(), keys_.end(), key);
51+
if (it == keys_.end())
52+
return 0;
53+
54+
keys_.erase(it);
55+
return map_.erase(key);
56+
}
57+
58+
class iterator {
59+
public:
60+
using iterator_category = std::forward_iterator_tag;
61+
using value_type = typename map_type::value_type;
62+
using difference_type = typename map_type::difference_type;
63+
using pointer = value_type*;
64+
using reference = value_type&;
65+
66+
typename key_map_type::iterator cur;
67+
map_type& map;
68+
69+
iterator(typename key_map_type::iterator it, map_type& map) :
70+
cur(it), map(map) {}
71+
72+
iterator& operator++() {
73+
++cur;
74+
return *this;
75+
}
76+
77+
iterator operator++(int) {
78+
iterator tmp = *this;
79+
++(*this);
80+
return tmp;
81+
}
82+
83+
bool operator==(const iterator& other) const { return cur == other.cur; }
84+
bool operator!=(const iterator& other) const { return cur != other.cur; }
85+
86+
pointer operator->() const { return &(*(map.find(*cur))); }
87+
reference operator*() const { return *(map.find(*cur)); }
88+
};
89+
90+
class const_iterator {
91+
public:
92+
using iterator_category = std::forward_iterator_tag;
93+
using value_type = typename map_type::value_type;
94+
using difference_type = typename map_type::difference_type;
95+
using pointer = const value_type*;
96+
using reference = const value_type&;
97+
98+
typename key_map_type::const_iterator cur;
99+
const map_type& map;
100+
101+
const_iterator(typename key_map_type::const_iterator it,
102+
const map_type& map) : cur(it), map(map) {}
103+
104+
const_iterator(const iterator& other) : cur(other.cur), map(other.map) {}
105+
106+
const_iterator& operator++() {
107+
++cur;
108+
return *this;
109+
}
110+
111+
const_iterator operator++(int) {
112+
const_iterator tmp = *this;
113+
++(*this);
114+
return tmp;
115+
}
116+
117+
bool operator==(const const_iterator& other) const { return cur == other.cur; }
118+
bool operator!=(const const_iterator& other) const { return cur != other.cur; }
119+
120+
pointer operator->() const { return &(*(map.find(*cur))); }
121+
reference operator*() const { return *(map.find(*cur)); }
122+
};
123+
124+
iterator begin() { return iterator(keys_.begin(), map_); }
125+
iterator end() { return iterator(keys_.end(), map_); }
126+
const_iterator begin() const { return const_iterator(keys_.begin(), map_); }
127+
const_iterator end() const { return const_iterator(keys_.end(), map_); }
128+
const_iterator cbegin() const { return const_iterator(keys_.cbegin(), map_); }
129+
const_iterator cend() const { return const_iterator(keys_.cend(), map_); }
130+
131+
iterator find(const key_type& key) {
132+
return iterator(std::find(keys_.begin(), keys_.end(), key), map_);
133+
}
134+
135+
const_iterator find(const key_type& key) const {
136+
return const_iterator(std::find(keys_.cbegin(), keys_.cend(), key), map_);
137+
}
138+
139+
std::pair<iterator, bool> insert(const value_type& pair) {
140+
const key_type& key = pair.first;
141+
142+
bool check = false;
143+
auto it = std::find(keys_.begin(), keys_.end(), key);
144+
if (it == keys_.end()) {
145+
keys_.push_back(key);
146+
it = std::prev(keys_.end());
147+
check = true;
148+
}
149+
map_.insert(pair);
150+
return {iterator(it, map_), check};
151+
}
152+
153+
std::pair<iterator, bool> insert(value_type&& pair) {
154+
const key_type& key = pair.first;
155+
156+
bool check = false;
157+
auto it = std::find(keys_.begin(), keys_.end(), key);
158+
if (it == keys_.end()) {
159+
keys_.push_back(key);
160+
it = std::prev(keys_.end());
161+
check = true;
162+
}
163+
map_.insert(pair);
164+
return {iterator(it, map_), check};
165+
}
166+
167+
protected:
168+
map_type map_;
169+
key_map_type keys_;
170+
};
171+
172+
#endif

core/src/G3Timestream.cxx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -650,10 +650,14 @@ template <class A> void G3TimestreamMap::serialize(A &ar, unsigned v)
650650
this->insert(std::pair<std::string, G3TimestreamPtr>(
651651
i->first, G3TimestreamPtr(new G3Timestream(
652652
i->second))));
653+
} else if (v < 4) {
654+
std::map<std::string, G3TimestreamPtr> oldmap;
655+
ar & cereal::make_nvp("map", oldmap);
656+
for (auto &it: oldmap)
657+
this->insert(it);
653658
} else {
654-
ar & cereal::make_nvp("map",
655-
cereal::base_class<std::map<std::string,
656-
G3TimestreamPtr> >(this));
659+
ar & cereal::make_nvp("map", map_);
660+
ar & cereal::make_nvp("keys", keys_);
657661
}
658662
if (v < 2) {
659663
// Load old timestreams with start/stop in the map instead of

0 commit comments

Comments
 (0)