]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Added a DescriptorSet class to manage an unordered collection of unique
authorAlex Rousskov <rousskov@measurement-factory.com>
Thu, 25 Sep 2008 17:22:12 +0000 (11:22 -0600)
committerAlex Rousskov <rousskov@measurement-factory.com>
Thu, 25 Sep 2008 17:22:12 +0000 (11:22 -0600)
descriptors.

DescriptorSet is used for half-closed descriptor monitoring. It might be
useful for deferred reads as well, but that remains to be seen.

DescriptorSet has O(1) complexity for search, insertion, and deletion. It uses
about 2*sizeof(int)*MaxFD bytes total. Splay tree that used to store
half-closed descriptors previously uses less RAM for small number of
descriptors but has O(log n) complexity. Same for std::set<int>, a potential
DescriptorSet replacement.

src/DescriptorSet.cc [new file with mode: 0644]
src/DescriptorSet.h [new file with mode: 0644]
src/Makefile.am

diff --git a/src/DescriptorSet.cc b/src/DescriptorSet.cc
new file mode 100644 (file)
index 0000000..3bd2b6a
--- /dev/null
@@ -0,0 +1,96 @@
+
+/*
+ * $Id$
+ *
+ * DEBUG: section 5     Comm
+ */
+
+#include "DescriptorSet.h"
+
+// pre-allocates descriptor store and index for Squid_MaxFD descriptors
+DescriptorSet::DescriptorSet(): descriptors_(NULL), index_(NULL),
+    capacity_(0), size_(0)
+{
+    // we allocate once and never realloc, at least for now
+    capacity_ = Squid_MaxFD;
+    descriptors_ = new int[capacity_];
+    index_ = new int[capacity_];
+
+    // fill index with -1s to be able to say whether a descriptor is present
+    // it is not essential to fill the descriptors, but it enables more checks
+    for (int i = 0; i < capacity_; ++i)
+        index_[i] = descriptors_[i] = -1;
+}
+
+DescriptorSet::~DescriptorSet()
+{
+    delete[] descriptors_;
+    delete[] index_;
+}
+
+/// adds if unique; returns true if added
+bool
+DescriptorSet::add(int fd)
+{
+    assert(0 <= fd && fd < capacity_); // \todo: replace with Must()
+
+    if (has(fd))
+        return false; // already have it
+
+    assert(size_ < capacity_); // \todo: replace with Must()
+    const int pos = size_++;
+    index_[fd] = pos;
+    descriptors_[pos] = fd;
+    return true; // really added
+}
+
+/// deletes if there; returns true if deleted
+bool
+DescriptorSet::del(int fd)
+{
+    assert(0 <= fd && fd < capacity_); // \todo: here and below, use Must()
+
+    if (!has(fd))
+        return false; // we do not have it
+
+    assert(!empty());
+    const int delPos = index_[fd];
+    assert(0 <= delPos && delPos < capacity_);
+
+    // move the last descriptor to the deleted fd position
+    // to avoid skipping deleted descriptors in pop()
+    const int lastPos = size_-1;
+    const int lastFd = descriptors_[lastPos];
+    assert(delPos <= lastPos); // may be the same
+    descriptors_[delPos] = lastFd;
+    index_[lastFd] = delPos;
+
+    descriptors_[lastPos] = -1;
+    index_[fd] = -1;
+    --size_;
+
+    return true; // really added
+}
+
+/// ejects one descriptor in unspecified order
+int
+DescriptorSet::pop()
+{
+    assert(!empty());
+    const int lastPos =--size_;
+    const int lastFd = descriptors_[lastPos];
+    assert(0 <= lastFd && lastFd < capacity_);
+
+    // cleanup
+    descriptors_[lastPos] = -1;
+    index_[lastFd] = -1;
+
+    return lastFd;
+}
+
+void
+DescriptorSet::print(std::ostream &os) const
+{
+    // \todo add "name" if the set is used for more than just half-closed FDs
+    os << size_ << " FDs";
+}
diff --git a/src/DescriptorSet.h b/src/DescriptorSet.h
new file mode 100644 (file)
index 0000000..44846c7
--- /dev/null
@@ -0,0 +1,58 @@
+#ifndef SQUID_DESCRIPTOR_SET_H
+#define SQUID_DESCRIPTOR_SET_H
+
+#include "squid.h"
+
+/** \ingroup Comm
+
+    \todo: Should we use std::set<int> with its flexibility? Our implementation
+    has constant overhead, which is smaller than log(n) of std::set.
+
+an unordered collection of unique descriptors with O(1) add/del/has ops */
+class DescriptorSet {
+public:
+    // for STL compatibility, should we decide to switch to std::set or similar
+    typedef const int *const_iterator;
+
+    DescriptorSet();
+    ~DescriptorSet();
+
+    /// checks whether fd is in the set
+    bool has(const int fd) const { return 0 <= fd && fd < capacity_ &&
+        index_[fd] >= 0; }
+
+    bool add(int fd); ///< adds if unique; returns true if added
+    bool del(int fd); ///< deletes if there; returns true if deleted
+    int pop(); ///< deletes and returns one descriptor, in unspecified order
+
+    bool empty() const { return !size_; } ///< number of descriptors in the set
+
+    /// begin iterator a la STL; may become invalid if the object is modified
+    const_iterator begin() const { return descriptors_; }
+    /// end iterator a la STL; may become invalid if the object is modified
+    const_iterator end() const { return begin() + size_; }
+
+    /// outputs debugging info about the set
+    void print(std::ostream &os) const;
+
+private:
+    // these would be easy to support when needed; prohibit for now
+    DescriptorSet(const DescriptorSet &s); // declared but undefined
+    DescriptorSet &operator =(const DescriptorSet &s); // declared, undefined
+
+    int *descriptors_; ///< descriptor values in random order
+    int *index_; ///< descriptor:position index into descriptors_
+    int capacity_; ///< total number of descriptor slots
+    int size_; ///< number of descriptors in the set
+};
+
+/// convenience wrapper to be used in debugs() context
+inline std::ostream &
+operator <<(std::ostream &os, const DescriptorSet &ds)
+{
+    ds.print(os);
+    return os;
+}
+
+#endif /* SQUID_DESCRIPTOR_SET_H */
+
index ea6a27a9fced3b2ab6729a85834562af2ffeae85..d16a8c4f37dc9a412cfc03e74e2a50e6448ff707 100644 (file)
@@ -427,6 +427,8 @@ libsquid_la_SOURCES = \
        comm.h \
        CommCalls.cc \
        CommCalls.h \
+       DescriptorSet.cc \
+       DescriptorSet.h \
        IPInterception.cc \
        IPInterception.h \
         ICAP/AsyncJob.cc \