Mercurial > hg > svcore
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 { |