comparison base/EventSeries.h @ 1631:b2048f350906 single-point

Switch EventSeries to using a vector for m_events, so as to allow indexed access
author Chris Cannam
date Tue, 12 Mar 2019 14:14:00 +0000
parents 1c21ddac220e
children 0890c10e5129
comparison
equal deleted inserted replaced
1630:73bda079567a 1631:b2048f350906
14 14
15 #ifndef SV_EVENT_SERIES_H 15 #ifndef SV_EVENT_SERIES_H
16 #define SV_EVENT_SERIES_H 16 #define SV_EVENT_SERIES_H
17 17
18 #include "Event.h" 18 #include "Event.h"
19 #include "XmlExportable.h"
19 20
20 #include <set> 21 #include <set>
21 22
22 //#define DEBUG_EVENT_SERIES 1 23 //#define DEBUG_EVENT_SERIES 1
23 24
38 * that is added requires updating all the seams within the extent of 39 * that is added requires updating all the seams within the extent of
39 * that event, taking a number of ordered-set updates proportional to 40 * that event, taking a number of ordered-set updates proportional to
40 * the number of events already existing within its extent. Add events 41 * the number of events already existing within its extent. Add events
41 * in order of start frame if possible. 42 * in order of start frame if possible.
42 */ 43 */
43 class EventSeries 44 class EventSeries : public XmlExportable
44 { 45 {
45 public: 46 public:
46 EventSeries() : m_count(0) { } 47 EventSeries() { }
47 ~EventSeries() =default; 48 ~EventSeries() =default;
48 49
49 EventSeries(const EventSeries &) =default; 50 EventSeries(const EventSeries &) =default;
50 EventSeries &operator=(const EventSeries &) =default; 51 EventSeries &operator=(const EventSeries &) =default;
51 EventSeries &operator=(EventSeries &&) =default; 52 EventSeries &operator=(EventSeries &&) =default;
52 53
53 bool operator==(const EventSeries &other) const { 54 bool operator==(const EventSeries &other) const {
54 return m_events == other.m_events; 55 return m_events == other.m_events;
55 } 56 }
56 57
57 void add(const Event &p) { 58 void clear();
58 59 void add(const Event &p);
59 bool isUnique = true; 60 void remove(const Event &p);
60 61 bool contains(const Event &p) const;
61 auto pitr = m_events.find(p); 62 bool isEmpty() const;
62 if (pitr != m_events.end()) { 63 int count() const;
63 isUnique = false;
64 }
65
66 ++m_events[p];
67 ++m_count;
68
69 if (p.hasDuration() && isUnique) {
70
71 const sv_frame_t frame = p.getFrame();
72 const sv_frame_t endFrame = p.getFrame() + p.getDuration();
73
74 createSeam(frame);
75 createSeam(endFrame);
76
77 // These calls must both succeed after calling createSeam above
78 const auto i0 = m_seams.find(frame);
79 const auto i1 = m_seams.find(endFrame);
80
81 for (auto i = i0; i != i1; ++i) {
82 if (i == m_seams.end()) {
83 SVCERR << "ERROR: EventSeries::add: "
84 << "reached end of seam map"
85 << endl;
86 break;
87 }
88 i->second.push_back(p);
89 }
90 }
91
92 #ifdef DEBUG_EVENT_SERIES
93 std::cerr << "after add:" << std::endl;
94 dumpEvents();
95 dumpSeams();
96 #endif
97 }
98
99 void remove(const Event &p) {
100
101 // If we are removing the last (unique) example of an event,
102 // then we also need to remove it from the seam map. If this
103 // is only one of multiple identical events, then we don't.
104 bool isUnique = false;
105
106 auto pitr = m_events.find(p);
107 if (pitr == m_events.end()) {
108 return; // we don't know this event
109 } else {
110 if (--(pitr->second) == 0) {
111 isUnique = true;
112 m_events.erase(pitr);
113 }
114 --m_count;
115 }
116
117 if (p.hasDuration() && isUnique) {
118
119 const sv_frame_t frame = p.getFrame();
120 const sv_frame_t endFrame = p.getFrame() + p.getDuration();
121
122 const auto i0 = m_seams.find(frame);
123 const auto i1 = m_seams.find(endFrame);
124
125 #ifdef DEBUG_EVENT_SERIES
126 // This should be impossible if we found p in m_events above
127 if (i0 == m_seams.end() || i1 == m_seams.end()) {
128 SVCERR << "ERROR: EventSeries::remove: either frame " << frame
129 << " or endFrame " << endFrame
130 << " for event not found in seam map: event is "
131 << p.toXmlString() << endl;
132 }
133 #endif
134
135 // Remove any and all instances of p from the seam map; we
136 // are only supposed to get here if we are removing the
137 // last instance of p from the series anyway
138
139 for (auto i = i0; i != i1; ++i) {
140 if (i == m_seams.end()) {
141 // This can happen only if we have a negative
142 // duration, which Event forbids
143 throw std::logic_error("unexpectedly reached end of map");
144 }
145 for (size_t j = 0; j < i->second.size(); ) {
146 if (i->second[j] == p) {
147 i->second.erase(i->second.begin() + j);
148 } else {
149 ++j;
150 }
151 }
152 }
153
154 // Tidy up by removing any entries that are now identical
155 // to their predecessors
156
157 std::vector<sv_frame_t> redundant;
158
159 auto pitr = m_seams.end();
160 if (i0 != m_seams.begin()) {
161 pitr = i0;
162 --pitr;
163 }
164
165 for (auto i = i0; i != m_seams.end(); ++i) {
166 if (pitr != m_seams.end() &&
167 seamsEqual(i->second, pitr->second)) {
168 redundant.push_back(i->first);
169 }
170 pitr = i;
171 if (i == i1) {
172 break;
173 }
174 }
175
176 for (sv_frame_t f: redundant) {
177 m_seams.erase(f);
178 }
179
180 // And remove any empty seams from the start of the map
181
182 while (m_seams.begin() != m_seams.end() &&
183 m_seams.begin()->second.empty()) {
184 m_seams.erase(m_seams.begin());
185 }
186 }
187
188 #ifdef DEBUG_EVENT_SERIES
189 std::cerr << "after remove:" << std::endl;
190 dumpEvents();
191 dumpSeams();
192 #endif
193 }
194
195 bool contains(const Event &p) {
196 return m_events.find(p) != m_events.end();
197 }
198
199 int count() const {
200 return m_count;
201 }
202
203 bool isEmpty() const {
204 return m_count == 0;
205 }
206
207 void clear() {
208 m_events.clear();
209 m_seams.clear();
210 m_count = 0;
211 }
212 64
213 /** 65 /**
214 * Retrieve all events any part of which falls within the span in 66 * Retrieve all events any part of which falls within the span in
215 * frames defined by the given frame f and duration d. 67 * frames defined by the given frame f and duration d.
216 * 68 *
226 * 0) is not equivalent to getEventsCovering(f). The latter 78 * 0) is not equivalent to getEventsCovering(f). The latter
227 * includes durationless events at f and events starting at f, 79 * includes durationless events at f and events starting at f,
228 * both of which are excluded from the former. 80 * both of which are excluded from the former.
229 */ 81 */
230 EventVector getEventsSpanning(sv_frame_t frame, 82 EventVector getEventsSpanning(sv_frame_t frame,
231 sv_frame_t duration) const { 83 sv_frame_t duration) const;
232 EventVector span;
233
234 const sv_frame_t start = frame;
235 const sv_frame_t end = frame + duration;
236
237 // first find any zero-duration events
238
239 auto pitr = m_events.lower_bound(Event(start, QString()));
240 while (pitr != m_events.end() && pitr->first.getFrame() < end) {
241 if (!pitr->first.hasDuration()) {
242 for (int i = 0; i < pitr->second; ++i) {
243 span.push_back(pitr->first);
244 }
245 }
246 ++pitr;
247 }
248
249 // now any non-zero-duration ones from the seam map
250
251 std::set<Event> found;
252 auto sitr = m_seams.lower_bound(start);
253 if (sitr == m_seams.end() || sitr->first > start) {
254 if (sitr != m_seams.begin()) {
255 --sitr;
256 }
257 }
258 while (sitr != m_seams.end() && sitr->first < end) {
259 for (const auto &p: sitr->second) {
260 found.insert(p);
261 }
262 ++sitr;
263 }
264 for (const auto &p: found) {
265 int n = m_events.at(p);
266 if (n < 1) {
267 throw std::logic_error("event is in seams but not events");
268 }
269 for (int i = 0; i < n; ++i) {
270 span.push_back(p);
271 }
272 }
273
274 return span;
275 }
276 84
277 /** 85 /**
278 * Retrieve all events that cover the given frame. An event without 86 * Retrieve all events that cover the given frame. An event without
279 * duration covers a frame if its own frame is equal to it. An event 87 * duration covers a frame if its own frame is equal to it. An event
280 * with duration covers a frame if its start frame is less than or 88 * with duration covers a frame if its start frame is less than or
281 * equal to it and its end frame (start + duration) is greater 89 * equal to it and its end frame (start + duration) is greater
282 * than it. 90 * than it.
283 */ 91 */
284 EventVector getEventsCovering(sv_frame_t frame) const { 92 EventVector getEventsCovering(sv_frame_t frame) const;
285 EventVector cover; 93
286 94 /**
287 // first find any zero-duration events 95 * Emit to XML as a dataset element.
288 96 */
289 auto pitr = m_events.lower_bound(Event(frame, QString())); 97 void toXml(QTextStream &out,
290 while (pitr != m_events.end() && pitr->first.getFrame() == frame) { 98 QString indent,
291 if (!pitr->first.hasDuration()) { 99 QString extraAttributes) const override;
292 for (int i = 0; i < pitr->second; ++i) { 100
293 cover.push_back(pitr->first);
294 }
295 }
296 ++pitr;
297 }
298
299 // now any non-zero-duration ones from the seam map
300
301 std::set<Event> found;
302 auto sitr = m_seams.lower_bound(frame);
303 if (sitr == m_seams.end() || sitr->first > frame) {
304 if (sitr != m_seams.begin()) {
305 --sitr;
306 }
307 }
308 if (sitr != m_seams.end() && sitr->first <= frame) {
309 for (const auto &p: sitr->second) {
310 found.insert(p);
311 }
312 ++sitr;
313 }
314 for (const auto &p: found) {
315 int n = m_events.at(p);
316 if (n < 1) {
317 throw std::logic_error("event is in seams but not events");
318 }
319 for (int i = 0; i < n; ++i) {
320 cover.push_back(p);
321 }
322 }
323
324 return cover;
325 }
326
327 private: 101 private:
328 /** 102 /**
329 * Total number of events in the series. Will exceed 103 * This vector contains all events in the series, in the normal
330 * m_events.size() if the series contains duplicate events. 104 * sort order. For backward compatibility we must support series
331 */ 105 * containing multiple instances of identical events, so
332 int m_count; 106 * consecutive events in this vector will not always be distinct.
333 107 * The vector is used in preference to a multiset or map<Event,
334 /** 108 * int> in order to allow indexing by "row number" as well as by
335 * The (ordered) Events map acts as a list of all the events in 109 * properties such as frame.
336 * the series. For reasons of backward compatibility, we have to 110 *
337 * handle series containing multiple instances of identical 111 * Because events are immutable, we do not have to worry about the
338 * events; the int indexed against each event records the number 112 * order changing once an event is inserted - we only add or
339 * of instances of that event. We do this in preference to using a 113 * delete them.
340 * multiset, in order to support the case in which we have 114 */
341 * obtained a set of distinct events (from the seam container 115 typedef std::vector<Event> Events;
342 * below) and then need to return the correct number of each.
343 *
344 * Because events are immutable, we never have to move one to a
345 * different "key" in this map - we only add or delete them or
346 * change their counts.
347 */
348 typedef std::map<Event, int> Events;
349 Events m_events; 116 Events m_events;
350 117
351 /** 118 /**
352 * The FrameEventMap maps from frame number to a set of events. In 119 * The FrameEventMap maps from frame number to a set of events. In
353 * the seam map this is used to represent the events that are 120 * the seam map this is used to represent the events that are
354 * active at that frame, either because they begin at that frame 121 * active at that frame, either because they begin at that frame
355 * or because they are continuing from an earlier frame. There is 122 * or because they are continuing from an earlier frame. There is
421 188
422 #ifdef DEBUG_EVENT_SERIES 189 #ifdef DEBUG_EVENT_SERIES
423 void dumpEvents() const { 190 void dumpEvents() const {
424 std::cerr << "EVENTS (" << m_events.size() << ") [" << std::endl; 191 std::cerr << "EVENTS (" << m_events.size() << ") [" << std::endl;
425 for (const auto &i: m_events) { 192 for (const auto &i: m_events) {
426 std::cerr << " " << i.second << "x " << i.first.toXmlString(); 193 std::cerr << " " << i.toXmlString();
427 } 194 }
428 std::cerr << "]" << std::endl; 195 std::cerr << "]" << std::endl;
429 } 196 }
430 197
431 void dumpSeams() const { 198 void dumpSeams() const {