comparison base/EventSeries.h @ 1616:de446dd905e6 single-point

Rework EventSeries to explicitly store counts of events (+ add comments explaining, among other things, why)
author Chris Cannam
date Fri, 08 Mar 2019 10:16:12 +0000
parents 24dc8cb42755
children bdc19a09a1f9
comparison
equal deleted inserted replaced
1615:24dc8cb42755 1616:de446dd905e6
21 21
22 //#define DEBUG_EVENT_SERIES 1 22 //#define DEBUG_EVENT_SERIES 1
23 23
24 /** 24 /**
25 * Container storing a series of events, with or without durations, 25 * Container storing a series of events, with or without durations,
26 * and supporting the ability to query which events span a given frame 26 * and supporting the ability to query which events are active at a
27 * time. To that end, in addition to the series of events, it stores a 27 * given frame or within a span of frames.
28 * series of "seam points", which are the frame positions at which the 28 *
29 * set of simultaneous events changes (i.e. an event of non-zero 29 * To that end, in addition to the series of events, it stores a
30 * duration starts or ends). These are updated when event is added or 30 * series of "seams", which are frame positions at which the set of
31 * removed. 31 * simultaneous events changes (i.e. an event of non-zero duration
32 * starts or ends) associated with a set of the events that are active
33 * at or from that position. These are updated when an event is added
34 * or removed.
32 */ 35 */
33 class EventSeries 36 class EventSeries
34 { 37 {
35 public: 38 public:
36 EventSeries() : m_count(0) { } 39 EventSeries() : m_count(0) { }
40 ~EventSeries() =default;
41
42 EventSeries(const EventSeries &) =default;
43 EventSeries &operator=(const EventSeries &) =default;
44 EventSeries &operator=(EventSeries &&) =default;
45
46 bool operator==(const EventSeries &other) const {
47 return m_events == other.m_events;
48 }
37 49
38 void add(const Event &p) { 50 void add(const Event &p) {
39 51
40 m_events.insert(p); 52 ++m_events[p];
41 ++m_count; 53 ++m_count;
42 54
43 if (p.hasDuration()) { 55 if (p.hasDuration()) {
44 sv_frame_t frame = p.getFrame(); 56 sv_frame_t frame = p.getFrame();
45 sv_frame_t endFrame = p.getFrame() + p.getDuration(); 57 sv_frame_t endFrame = p.getFrame() + p.getDuration();
46 58
47 createSeam(frame); 59 createSeam(frame);
48 createSeam(endFrame); 60 createSeam(endFrame);
49 61
50 auto i0 = m_seams.find(frame); // must succeed after createSeam 62 // These calls must both succeed after calling createSeam above
51 auto i1 = m_seams.find(endFrame); // likewise 63 const auto i0 = m_seams.find(frame);
64 const auto i1 = m_seams.find(endFrame);
52 65
53 for (auto i = i0; i != i1; ++i) { 66 for (auto i = i0; i != i1; ++i) {
54 if (i == m_seams.end()) { 67 if (i == m_seams.end()) {
55 SVCERR << "ERROR: EventSeries::add: " 68 SVCERR << "ERROR: EventSeries::add: "
56 << "reached end of seam map" 69 << "reached end of seam map"
68 #endif 81 #endif
69 } 82 }
70 83
71 void remove(const Event &p) { 84 void remove(const Event &p) {
72 85
73 // erase first itr that matches p; if there is more than one 86 // If we are removing the last (unique) example of an event,
74 // p, erase(p) would remove all of them, but we only want to 87 // then we also need to remove it from the seam map. If this
75 // remove (any) one 88 // is only one of multiple identical events, then we don't.
89 bool isUnique = false;
90
76 auto pitr = m_events.find(p); 91 auto pitr = m_events.find(p);
77 if (pitr == m_events.end()) { 92 if (pitr == m_events.end()) {
78 return; // we don't know this event 93 return; // we don't know this event
79 } else { 94 } else {
80 m_events.erase(pitr); 95 if (--(pitr->second) == 0) {
96 isUnique = true;
97 m_events.erase(pitr);
98 }
81 --m_count; 99 --m_count;
82 } 100 }
83 101
84 if (p.hasDuration()) { 102 if (p.hasDuration() && isUnique) {
103
85 sv_frame_t frame = p.getFrame(); 104 sv_frame_t frame = p.getFrame();
86 sv_frame_t endFrame = p.getFrame() + p.getDuration(); 105 sv_frame_t endFrame = p.getFrame() + p.getDuration();
87 106
88 auto i0 = m_seams.find(frame); 107 const auto i0 = m_seams.find(frame);
89 auto i1 = m_seams.find(endFrame); 108 const auto i1 = m_seams.find(endFrame);
90 109
91 #ifdef DEBUG_EVENT_SERIES 110 #ifdef DEBUG_EVENT_SERIES
92 // This should be impossible if we found p in m_events above 111 // This should be impossible if we found p in m_events above
93 if (i0 == m_seams.end() || i1 == m_seams.end()) { 112 if (i0 == m_seams.end() || i1 == m_seams.end()) {
94 SVCERR << "ERROR: EventSeries::remove: either frame " << frame 113 SVCERR << "ERROR: EventSeries::remove: either frame " << frame
99 #endif 118 #endif
100 119
101 for (auto i = i0; i != i1; ++i) { 120 for (auto i = i0; i != i1; ++i) {
102 if (i == m_seams.end()) { 121 if (i == m_seams.end()) {
103 // This can happen only if we have a negative 122 // This can happen only if we have a negative
104 // duration, which Event forbids, but we don't 123 // duration, which Event forbids
105 // protect against it in this class, so we'll 124 throw std::logic_error("unexpectedly reached end of map");
106 // leave this check in 125 }
107 SVCERR << "ERROR: EventSeries::remove: " 126
108 << "reached end of seam map" 127 i->second.erase(p);
109 << endl; 128 }
129
130 // Tidy up by removing any entries that are now identical
131 // to their predecessors
132
133 std::vector<sv_frame_t> redundant;
134
135 auto pitr = m_seams.end();
136 if (i0 != m_seams.begin()) {
137 pitr = i0;
138 --pitr;
139 }
140
141 for (auto i = i0; i != m_seams.end(); ++i) {
142 if (pitr != m_seams.end() && i->second == pitr->second) {
143 redundant.push_back(i->first);
144 }
145 pitr = i;
146 if (i == i1) {
110 break; 147 break;
111 } 148 }
112 // Can't just erase(p) as that would erase all of 149 }
113 // them, if there are several identical ones 150
114 auto si = i->second.find(p); 151 for (sv_frame_t f: redundant) {
115 if (si != i->second.end()) { 152 m_seams.erase(f);
116 i->second.erase(si); 153 }
117 }
118 }
119
120 // Shall we "garbage-collect" here? We could be leaving
121 // lots of empty event-sets, or consecutive identical
122 // ones, which are a pure irrelevance that take space and
123 // slow us down. But a lot depends on whether callers ever
124 // really delete anything much.
125 } 154 }
126 155
127 #ifdef DEBUG_EVENT_SERIES 156 #ifdef DEBUG_EVENT_SERIES
128 std::cerr << "after remove:" << std::endl; 157 std::cerr << "after remove:" << std::endl;
129 dumpEvents(); 158 dumpEvents();
148 m_seams.clear(); 177 m_seams.clear();
149 m_count = 0; 178 m_count = 0;
150 } 179 }
151 180
152 /** 181 /**
153 * Retrieve all events that span the given frame. A event without 182 * Retrieve all events any part of which falls within the span in
154 * duration spans a frame if its own frame is equal to it. A event 183 * frames defined by the given frame f and duration d.
155 * with duration spans a frame if its start frame is less than or 184 *
185 * - An event without duration is within the span if its own frame
186 * is greater than or equal to f and less than f + d.
187 *
188 * - An event with duration is within the span if its start frame
189 * is less than f + d and its start frame plus its duration is
190 * greater than f.
191 *
192 * Note that getEventsSpanning(f, 0) is not equivalent to
193 * getEventsCovering(f) - they have different behaviour in the
194 * case of events starting exactly at f, which are included in the
195 * latter but not the former.
196 */
197 EventVector getEventsSpanning(sv_frame_t f, sv_frame_t d) const {
198 EventVector span;
199
200 sv_frame_t start = f;
201 sv_frame_t end = f + d;
202
203 // first find any zero-duration events
204
205 auto pitr = m_events.lower_bound(Event(start, QString()));
206 while (pitr != m_events.end() && pitr->first.getFrame() < end) {
207 if (!pitr->first.hasDuration()) {
208 for (int i = 0; i < pitr->second; ++i) {
209 span.push_back(pitr->first);
210 }
211 }
212 ++pitr;
213 }
214
215 // now any non-zero-duration ones from the seam map
216
217 std::set<Event> found;
218 auto sitr = m_seams.lower_bound(start);
219 if (sitr == m_seams.end() || sitr->first > start) {
220 if (sitr != m_seams.begin()) {
221 --sitr;
222 }
223 }
224 while (sitr != m_seams.end() && sitr->first < end) {
225 for (const auto &p: sitr->second) {
226 found.insert(p);
227 }
228 ++sitr;
229 }
230 for (const auto &p: found) {
231 int n = m_events.at(p);
232 if (n < 1) {
233 throw std::logic_error("event is in seams but not events");
234 }
235 for (int i = 0; i < n; ++i) {
236 span.push_back(p);
237 }
238 }
239
240 return span;
241 }
242
243 /**
244 * Retrieve all events that cover the given frame. An event without
245 * duration covers a frame if its own frame is equal to it. An event
246 * with duration covers a frame if its start frame is less than or
156 * equal to it and its end frame (start + duration) is greater 247 * equal to it and its end frame (start + duration) is greater
157 * than it. 248 * than it.
158 */ 249 */
159 EventVector getEventsSpanning(sv_frame_t frame) const { 250 EventVector getEventsCovering(sv_frame_t frame) const {
160 EventVector span; 251 EventVector cover;
161 252
162 // first find any zero-duration events 253 // first find any zero-duration events
254
163 auto pitr = m_events.lower_bound(Event(frame, QString())); 255 auto pitr = m_events.lower_bound(Event(frame, QString()));
164 if (pitr != m_events.end()) { 256 while (pitr != m_events.end() && pitr->first.getFrame() == frame) {
165 while (pitr->getFrame() == frame) { 257 if (!pitr->first.hasDuration()) {
166 if (!pitr->hasDuration()) { 258 for (int i = 0; i < pitr->second; ++i) {
167 span.push_back(*pitr); 259 cover.push_back(pitr->first);
168 } 260 }
169 ++pitr; 261 }
170 } 262 ++pitr;
171 } 263 }
172 264
173 // now any non-zero-duration ones from the seam map 265 // now any non-zero-duration ones from the seam map
266
174 auto sitr = m_seams.lower_bound(frame); 267 auto sitr = m_seams.lower_bound(frame);
175 if (sitr == m_seams.end() || sitr->first > frame) { 268 if (sitr == m_seams.end() || sitr->first > frame) {
176 if (sitr != m_seams.begin()) { 269 if (sitr != m_seams.begin()) {
177 --sitr; 270 --sitr;
178 } 271 }
179 } 272 }
180 if (sitr != m_seams.end() && sitr->first <= frame) { 273 if (sitr != m_seams.end() && sitr->first <= frame) {
181 for (auto p: sitr->second) { 274 for (const auto &p: sitr->second) {
182 span.push_back(p); 275 int n = m_events.at(p);
183 } 276 if (n < 1) {
184 } 277 throw std::logic_error("event is in seams but not events");
185 278 }
186 return span; 279 for (int i = 0; i < n; ++i) {
280 cover.push_back(p);
281 }
282 }
283 }
284
285 return cover;
187 } 286 }
188 287
189 private: 288 private:
289 /**
290 * Total number of events in the series. Will exceed
291 * m_events.size() if the series contains duplicate events.
292 */
190 int m_count; 293 int m_count;
191 294
192 typedef std::multiset<Event> EventMultiset; 295 /**
193 EventMultiset m_events; 296 * The (ordered) Events map acts as a list of all the events in
194 297 * the series. For reasons of backward compatibility, we have to
195 typedef std::map<sv_frame_t, std::multiset<Event>> FrameEventsMap; 298 * handle series containing multiple instances of identical
196 FrameEventsMap m_seams; 299 * events; the int indexed against each event records the number
300 * of instances of that event. We do this in preference to using a
301 * multiset, in order to support the case in which we have
302 * obtained a set of distinct events (from the seam container
303 * below) and then need to return the correct number of each.
304 *
305 * Because events are immutable, we never have to move one to a
306 * different "key" in this map - we only add or delete them or
307 * change their counts.
308 */
309 typedef std::map<Event, int> Events;
310 Events m_events;
311
312 /**
313 * The FrameEventMap maps from frame number to a set of events. In
314 * the seam map this is used to represent the events that are
315 * active at that frame, either because they begin at that frame
316 * or because they are continuing from an earlier frame. There is
317 * an entry here for each frame at which an event starts or ends,
318 * with the event appearing in all entries from its start time
319 * onward and disappearing again at its end frame.
320 *
321 * Only events with duration appear in this map; point events
322 * appear only in m_events.
323 */
324 typedef std::map<sv_frame_t, std::set<Event>> FrameEventMap;
325 FrameEventMap m_seams;
197 326
198 /** Create a seam at the given frame, copying from the prior seam 327 /** Create a seam at the given frame, copying from the prior seam
199 * if there is one. If a seam already exists at the given frame, 328 * if there is one. If a seam already exists at the given frame,
200 * leave it untouched. 329 * leave it untouched.
201 */ 330 */
216 } 345 }
217 346
218 #ifdef DEBUG_EVENT_SERIES 347 #ifdef DEBUG_EVENT_SERIES
219 void dumpEvents() const { 348 void dumpEvents() const {
220 std::cerr << "EVENTS [" << std::endl; 349 std::cerr << "EVENTS [" << std::endl;
221 for (const auto &p: m_events) { 350 for (const auto &i: m_events) {
222 std::cerr << p.toXmlString(" "); 351 std::cerr << i.first.toXmlString(" ");
223 } 352 }
224 std::cerr << "]" << std::endl; 353 std::cerr << "]" << std::endl;
225 } 354 }
226 355
227 void dumpSeams() const { 356 void dumpSeams() const {