Skip to content

Commit c6bec9a

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 in converting between G3SuperTimestream and G3TimestreamMap objects, as the former are not guaranteed to be ordered alphabetically.
1 parent 561ab9c commit c6bec9a

File tree

3 files changed

+199
-11
lines changed

3 files changed

+199
-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;
@@ -254,8 +256,6 @@ class G3TimestreamMap : public G3FrameObject,
254256
void FromBuffer(const std::vector<std::string>& keys, std::size_t n_samples,
255257
std::shared_ptr<SampleType[]> data, G3Time start, G3Time stop,
256258
G3Timestream::TimestreamUnits units=G3Timestream::None, int compression_level=0) {
257-
if (!std::is_sorted(keys.begin(), keys.end()))
258-
throw std::runtime_error("G3TimestreamMap::MakeCompact: keys must be sorted");
259259
const auto data_type=G3Timestream::TimeStreamTypeResolver<SampleType>::type_tag;
260260
std::size_t offset = 0;
261261
for (const auto& key : keys) {
@@ -268,7 +268,7 @@ class G3TimestreamMap : public G3FrameObject,
268268
ts->data_ = data.get() + offset;
269269
ts->data_type_ = data_type;
270270
ts->len_ = n_samples;
271-
emplace(key, std::move(ts));
271+
insert({key, std::move(ts)});
272272
offset += n_samples;
273273
}
274274
}
@@ -285,7 +285,7 @@ namespace cereal {
285285
}
286286

287287
G3_SERIALIZABLE(G3Timestream, 3);
288-
G3_SERIALIZABLE(G3TimestreamMap, 3);
288+
G3_SERIALIZABLE(G3TimestreamMap, 4);
289289

290290
#endif
291291

core/include/core/containers.h

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
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 = map_.find(key);
51+
if (it == map_.end())
52+
return 0;
53+
54+
map_.erase(it);
55+
56+
auto it_order = std::find(keys_.begin(), keys_.end(), key);
57+
if (it_order != keys_.end())
58+
keys_.erase(it_order);
59+
return 1;
60+
}
61+
62+
class const_iterator;
63+
64+
class iterator {
65+
public:
66+
using iterator_category = std::forward_iterator_tag;
67+
using value_type = typename map_type::value_type;
68+
using difference_type = typename map_type::difference_type;
69+
using pointer = value_type*;
70+
using reference = value_type&;
71+
72+
typename key_map_type::iterator cur;
73+
map_type& map;
74+
75+
iterator(typename key_map_type::iterator it, map_type& map) :
76+
cur(it), map(map) {}
77+
78+
iterator& operator++() {
79+
++cur;
80+
return *this;
81+
}
82+
83+
iterator operator++(int) {
84+
iterator tmp = *this;
85+
++(*this);
86+
return tmp;
87+
}
88+
89+
bool operator==(const iterator& other) const { return cur == other.cur; }
90+
bool operator!=(const iterator& other) const { return cur != other.cur; }
91+
bool operator==(const const_iterator& other) const { return cur == other.cur; }
92+
bool operator!=(const const_iterator& other) const { return cur != other.cur; }
93+
94+
pointer operator->() const { return &(*(map.find(*cur))); }
95+
reference operator*() const { return *(map.find(*cur)); }
96+
};
97+
98+
class const_iterator {
99+
public:
100+
using iterator_category = std::forward_iterator_tag;
101+
using value_type = typename map_type::value_type;
102+
using difference_type = typename map_type::difference_type;
103+
using pointer = const value_type*;
104+
using reference = const value_type&;
105+
106+
typename key_map_type::const_iterator cur;
107+
const map_type& map;
108+
109+
const_iterator(typename key_map_type::const_iterator it,
110+
const map_type& map) : cur(it), map(map) {}
111+
112+
const_iterator(const iterator& other) : cur(other.cur), map(other.map) {}
113+
114+
const_iterator& operator++() {
115+
++cur;
116+
return *this;
117+
}
118+
119+
const_iterator operator++(int) {
120+
const_iterator tmp = *this;
121+
++(*this);
122+
return tmp;
123+
}
124+
125+
bool operator==(const const_iterator& other) const { return cur == other.cur; }
126+
bool operator!=(const const_iterator& other) const { return cur != other.cur; }
127+
bool operator==(const iterator& other) const { return cur == other.cur; }
128+
bool operator!=(const iterator& other) const { return cur != other.cur; }
129+
130+
pointer operator->() const { return &(*(map.find(*cur))); }
131+
reference operator*() const { return *(map.find(*cur)); }
132+
};
133+
134+
iterator begin() { return iterator(keys_.begin(), map_); }
135+
iterator end() { return iterator(keys_.end(), map_); }
136+
const_iterator begin() const { return const_iterator(keys_.begin(), map_); }
137+
const_iterator end() const { return const_iterator(keys_.end(), map_); }
138+
const_iterator cbegin() const { return const_iterator(keys_.cbegin(), map_); }
139+
const_iterator cend() const { return const_iterator(keys_.cend(), map_); }
140+
141+
iterator find(const key_type& key) {
142+
auto it = map_.find(key);
143+
if (it == map_.end())
144+
return end();
145+
return iterator(std::find(keys_.begin(), keys_.end(), it->first), map_);
146+
}
147+
148+
const_iterator find(const key_type& key) const {
149+
auto it = map_.find(key);
150+
if (it == map_.cend())
151+
return cend();
152+
return const_iterator(std::find(keys_.cbegin(), keys_.cend(), it->first), map_);
153+
}
154+
155+
std::pair<iterator, bool> insert(const value_type& pair) {
156+
const key_type& key = pair.first;
157+
158+
bool check = false;
159+
if (map_.find(key) == map_.end()) {
160+
keys_.push_back(key);
161+
check = true;
162+
}
163+
map_.insert(pair);
164+
return {iterator(std::find(keys_.begin(), keys_.end(), key), map_), check};
165+
}
166+
167+
std::pair<iterator, bool> insert(value_type&& pair) {
168+
const key_type& key = pair.first;
169+
170+
bool check = false;
171+
if (map_.find(key) == map_.end()) {
172+
keys_.push_back(key);
173+
check = true;
174+
}
175+
map_.insert(pair);
176+
return {iterator(std::find(keys_.begin(), keys_.end(), key), map_), check};
177+
}
178+
179+
protected:
180+
map_type map_;
181+
key_map_type keys_;
182+
};
183+
184+
#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)