diff 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
line wrap: on
line diff
--- a/base/EventSeries.h	Tue Mar 12 14:11:06 2019 +0000
+++ b/base/EventSeries.h	Tue Mar 12 14:14:00 2019 +0000
@@ -16,6 +16,7 @@
 #define SV_EVENT_SERIES_H
 
 #include "Event.h"
+#include "XmlExportable.h"
 
 #include <set>
 
@@ -40,10 +41,10 @@
  * the number of events already existing within its extent. Add events
  * in order of start frame if possible.
  */
-class EventSeries
+class EventSeries : public XmlExportable
 {
 public:
-    EventSeries() : m_count(0) { }
+    EventSeries() { }
     ~EventSeries() =default;
 
     EventSeries(const EventSeries &) =default;
@@ -54,161 +55,12 @@
         return m_events == other.m_events;
     }
     
-    void add(const Event &p) {
-
-        bool isUnique = true;
-
-        auto pitr = m_events.find(p);
-        if (pitr != m_events.end()) {
-            isUnique = false;
-        }
-
-        ++m_events[p];
-        ++m_count;
-
-        if (p.hasDuration() && isUnique) {
-
-            const sv_frame_t frame = p.getFrame();
-            const sv_frame_t endFrame = p.getFrame() + p.getDuration();
-
-            createSeam(frame);
-            createSeam(endFrame);
-
-            // These calls must both succeed after calling createSeam above
-            const auto i0 = m_seams.find(frame);
-            const auto i1 = m_seams.find(endFrame);
-
-            for (auto i = i0; i != i1; ++i) {
-                if (i == m_seams.end()) {
-                    SVCERR << "ERROR: EventSeries::add: "
-                           << "reached end of seam map"
-                           << endl;
-                    break;
-                }
-                i->second.push_back(p);
-            }
-        }
-
-#ifdef DEBUG_EVENT_SERIES
-        std::cerr << "after add:" << std::endl;
-        dumpEvents();
-        dumpSeams();
-#endif
-    }
-
-    void remove(const Event &p) {
-
-        // If we are removing the last (unique) example of an event,
-        // then we also need to remove it from the seam map. If this
-        // is only one of multiple identical events, then we don't.
-        bool isUnique = false;
-        
-        auto pitr = m_events.find(p);
-        if (pitr == m_events.end()) {
-            return; // we don't know this event
-        } else {
-            if (--(pitr->second) == 0) {
-                isUnique = true;
-                m_events.erase(pitr);
-            }
-            --m_count;
-        }
-
-        if (p.hasDuration() && isUnique) {
-            
-            const sv_frame_t frame = p.getFrame();
-            const sv_frame_t endFrame = p.getFrame() + p.getDuration();
-
-            const auto i0 = m_seams.find(frame);
-            const auto i1 = m_seams.find(endFrame);
-
-#ifdef DEBUG_EVENT_SERIES
-            // This should be impossible if we found p in m_events above
-            if (i0 == m_seams.end() || i1 == m_seams.end()) {
-                SVCERR << "ERROR: EventSeries::remove: either frame " << frame
-                       << " or endFrame " << endFrame
-                       << " for event not found in seam map: event is "
-                       << p.toXmlString() << endl;
-            }
-#endif
-
-            // Remove any and all instances of p from the seam map; we
-            // are only supposed to get here if we are removing the
-            // last instance of p from the series anyway
-            
-            for (auto i = i0; i != i1; ++i) {
-                if (i == m_seams.end()) {
-                    // This can happen only if we have a negative
-                    // duration, which Event forbids
-                    throw std::logic_error("unexpectedly reached end of map");
-                }
-                for (size_t j = 0; j < i->second.size(); ) {
-                    if (i->second[j] == p) {
-                        i->second.erase(i->second.begin() + j);
-                    } else {
-                        ++j;
-                    }
-                }
-            }
-
-            // Tidy up by removing any entries that are now identical
-            // to their predecessors
-            
-            std::vector<sv_frame_t> redundant;
-
-            auto pitr = m_seams.end();
-            if (i0 != m_seams.begin()) {
-                pitr = i0;
-                --pitr;
-            }
-
-            for (auto i = i0; i != m_seams.end(); ++i) {
-                if (pitr != m_seams.end() &&
-                    seamsEqual(i->second, pitr->second)) {
-                    redundant.push_back(i->first);
-                }
-                pitr = i;
-                if (i == i1) {
-                    break;
-                }
-            }
-
-            for (sv_frame_t f: redundant) {
-                m_seams.erase(f);
-            }
-
-            // And remove any empty seams from the start of the map
-            
-            while (m_seams.begin() != m_seams.end() &&
-                   m_seams.begin()->second.empty()) {
-                m_seams.erase(m_seams.begin());
-            }
-        }
-
-#ifdef DEBUG_EVENT_SERIES
-        std::cerr << "after remove:" << std::endl;
-        dumpEvents();
-        dumpSeams();
-#endif
-    }
-
-    bool contains(const Event &p) {
-        return m_events.find(p) != m_events.end();
-    }
-
-    int count() const {
-        return m_count;
-    }
-
-    bool isEmpty() const {
-        return m_count == 0;
-    }
-    
-    void clear() {
-        m_events.clear();
-        m_seams.clear();
-        m_count = 0;
-    }
+    void clear();
+    void add(const Event &p);
+    void remove(const Event &p);
+    bool contains(const Event &p) const;
+    bool isEmpty() const;
+    int count() const;
 
     /**
      * Retrieve all events any part of which falls within the span in
@@ -228,51 +80,7 @@
      * both of which are excluded from the former.
      */
     EventVector getEventsSpanning(sv_frame_t frame,
-                                  sv_frame_t duration) const {
-        EventVector span;
-
-        const sv_frame_t start = frame;
-        const sv_frame_t end = frame + duration;
-        
-        // first find any zero-duration events
-        
-        auto pitr = m_events.lower_bound(Event(start, QString()));
-        while (pitr != m_events.end() && pitr->first.getFrame() < end) {
-            if (!pitr->first.hasDuration()) {
-                for (int i = 0; i < pitr->second; ++i) {
-                    span.push_back(pitr->first);
-                }
-            }
-            ++pitr;
-        }
-
-        // now any non-zero-duration ones from the seam map
-
-        std::set<Event> found;
-        auto sitr = m_seams.lower_bound(start);
-        if (sitr == m_seams.end() || sitr->first > start) {
-            if (sitr != m_seams.begin()) {
-                --sitr;
-            }                
-        }
-        while (sitr != m_seams.end() && sitr->first < end) {
-            for (const auto &p: sitr->second) {
-                found.insert(p);
-            }
-            ++sitr;
-        }
-        for (const auto &p: found) {
-            int n = m_events.at(p);
-            if (n < 1) {
-                throw std::logic_error("event is in seams but not events");
-            }
-            for (int i = 0; i < n; ++i) {
-                span.push_back(p);
-            }
-        }
-            
-        return span;
-    }
+                                  sv_frame_t duration) const;
 
     /**
      * Retrieve all events that cover the given frame. An event without
@@ -281,73 +89,32 @@
      * equal to it and its end frame (start + duration) is greater
      * than it.
      */
-    EventVector getEventsCovering(sv_frame_t frame) const {
-        EventVector cover;
+    EventVector getEventsCovering(sv_frame_t frame) const;
 
-        // first find any zero-duration events
-        
-        auto pitr = m_events.lower_bound(Event(frame, QString()));
-        while (pitr != m_events.end() && pitr->first.getFrame() == frame) {
-            if (!pitr->first.hasDuration()) {
-                for (int i = 0; i < pitr->second; ++i) {
-                    cover.push_back(pitr->first);
-                }
-            }
-            ++pitr;
-        }
-
-        // now any non-zero-duration ones from the seam map
-        
-        std::set<Event> found;
-        auto sitr = m_seams.lower_bound(frame);
-        if (sitr == m_seams.end() || sitr->first > frame) {
-            if (sitr != m_seams.begin()) {
-                --sitr;
-            }                
-        }
-        if (sitr != m_seams.end() && sitr->first <= frame) {
-            for (const auto &p: sitr->second) {
-                found.insert(p);
-            }
-            ++sitr;
-        }
-        for (const auto &p: found) {
-            int n = m_events.at(p);
-            if (n < 1) {
-                throw std::logic_error("event is in seams but not events");
-            }
-            for (int i = 0; i < n; ++i) {
-                cover.push_back(p);
-            }
-        }
-        
-        return cover;
-    }
-
+    /**
+     * Emit to XML as a dataset element.
+     */
+    void toXml(QTextStream &out,
+               QString indent,
+               QString extraAttributes) const override;
+    
 private:
     /**
-     * Total number of events in the series. Will exceed
-     * m_events.size() if the series contains duplicate events.
+     * This vector contains all events in the series, in the normal
+     * sort order. For backward compatibility we must support series
+     * containing multiple instances of identical events, so
+     * consecutive events in this vector will not always be distinct.
+     * The vector is used in preference to a multiset or map<Event,
+     * int> in order to allow indexing by "row number" as well as by
+     * properties such as frame.
+     * 
+     * Because events are immutable, we do not have to worry about the
+     * order changing once an event is inserted - we only add or
+     * delete them.
      */
-    int m_count;
-
-    /**
-     * The (ordered) Events map acts as a list of all the events in
-     * the series. For reasons of backward compatibility, we have to
-     * handle series containing multiple instances of identical
-     * events; the int indexed against each event records the number
-     * of instances of that event. We do this in preference to using a
-     * multiset, in order to support the case in which we have
-     * obtained a set of distinct events (from the seam container
-     * below) and then need to return the correct number of each.
-     *
-     * Because events are immutable, we never have to move one to a
-     * different "key" in this map - we only add or delete them or
-     * change their counts.
-     */
-    typedef std::map<Event, int> Events;
+    typedef std::vector<Event> Events;
     Events m_events;
-
+    
     /**
      * The FrameEventMap maps from frame number to a set of events. In
      * the seam map this is used to represent the events that are
@@ -423,7 +190,7 @@
     void dumpEvents() const {
         std::cerr << "EVENTS (" << m_events.size() << ") [" << std::endl;
         for (const auto &i: m_events) {
-            std::cerr << "  " << i.second << "x " << i.first.toXmlString();
+            std::cerr << "  " << i.toXmlString();
         }
         std::cerr << "]" << std::endl;
     }