Browse Source

Merge branch 'work/perf/obuffer'

Michal 'vorner' Vaner 14 years ago
parent
commit
5eeba20344
2 changed files with 152 additions and 25 deletions
  1. 103 25
      src/lib/dns/buffer.h
  2. 49 0
      src/lib/dns/tests/buffer_unittest.cc

+ 103 - 25
src/lib/dns/buffer.h

@@ -15,6 +15,8 @@
 #ifndef __BUFFER_H
 #define __BUFFER_H 1
 
+#include <cstdlib>
+#include <cstring>
 #include <vector>
 
 #include <string.h>
@@ -286,15 +288,58 @@ public:
     /// \brief Constructor from the initial size of the buffer.
     ///
     /// \param len The initial length of the buffer in bytes.
-    OutputBuffer(size_t len) { data_.reserve(len); }
+    OutputBuffer(size_t len) :
+        buffer_(NULL),
+        size_(0),
+        allocated_(len)
+    {
+        // We use malloc and free instead of C++ new[] and delete[].
+        // This way we can use realloc, which may in fact do it without a copy.
+        buffer_ = static_cast<uint8_t*>(malloc(allocated_));
+        if (buffer_ == NULL && len != 0) {
+            throw std::bad_alloc();
+        }
+    }
+
+    /// \brief Copy constructor
+    OutputBuffer(const OutputBuffer& other) :
+        buffer_(NULL),
+        size_(other.size_),
+        allocated_(other.allocated_)
+    {
+        buffer_ = static_cast<uint8_t*>(malloc(allocated_));
+        if (buffer_ == NULL && allocated_ != 0) {
+            throw std::bad_alloc();
+        }
+        memcpy(buffer_, other.buffer_, size_);
+    }
+
+    /// \brief Destructor
+    ~ OutputBuffer() {
+        free(buffer_);
+    }
     //@}
 
+    /// \brief Assignment operator
+    OutputBuffer& operator =(const OutputBuffer& other) {
+        uint8_t* newbuff(static_cast<uint8_t*>(malloc(other.allocated_)));
+        if (newbuff == NULL && other.allocated_ != 0) {
+            throw std::bad_alloc();
+        }
+        free(buffer_);
+        buffer_ = newbuff;
+        size_ = other.size_;
+        allocated_ = other.allocated_;
+        memcpy(buffer_, other.buffer_, size_);
+        return (*this);
+    }
+
     ///
     /// \name Getter Methods
     ///
     //@{
     /// \brief Return the current capacity of the buffer.
-    size_t getCapacity() const { return (data_.capacity()); }
+    size_t getCapacity() const { return (allocated_); }
     /// \brief Return a pointer to the head of the data stored in the buffer.
     ///
     /// The caller can assume that the subsequent \c getLength() bytes are
@@ -302,21 +347,21 @@ public:
     ///
     /// Note: The pointer returned by this method may be invalidated after a
     /// subsequent write operation.
-    const void* getData() const { return (&data_[0]); }
+    const void* getData() const { return (buffer_); }
     /// \brief Return the length of data written in the buffer.
-    size_t getLength() const { return (data_.size()); }
+    size_t getLength() const { return (size_); }
     /// \brief Return the value of the buffer at the specified position.
     ///
     /// \c pos must specify the valid position of the buffer; otherwise an
     /// exception class of \c InvalidBufferPosition will be thrown.
     ///
     /// \param pos The position in the buffer to be returned.
-    const uint8_t& operator[](size_t pos) const
+    uint8_t operator[](size_t pos) const
     {
-        if (pos >= data_.size()) {
+        if (pos >= size_) {
             isc_throw(InvalidBufferPosition, "read at invalid position");
         }
-        return (data_[pos]);
+        return (buffer_[pos]);
     }
     //@}
 
@@ -330,7 +375,10 @@ public:
     /// This method is provided as a shortcut to make a hole in the buffer
     /// that is to be filled in later, e.g, by \ref writeUint16At().
     /// \param len The length of the gap to be inserted in bytes.
-    void skip(size_t len) { data_.insert(data_.end(), len, 0); }
+    void skip(size_t len) {
+        ensureAllocated(size_ + len);
+        size_ += len;
+    }
 
     /// \brief Trim the specified length of data from the end of the buffer.
     ///
@@ -341,20 +389,23 @@ public:
     /// \param len The length of data that should be trimmed.
     void trim(size_t len)
     {
-        if (len > data_.size()) {
+        if (len > size_) {
             isc_throw(OutOfRange, "trimming too large from output buffer");
         }
-        data_.resize(data_.size() - len);
+        size_ -= len;
     }
     /// \brief Clear buffer content.
     ///
     /// This method can be used to re-initialize and reuse the buffer without
     /// constructing a new one.
-    void clear() { data_.clear(); }
+    void clear() { size_ = 0; }
     /// \brief Write an unsigned 8-bit integer into the buffer.
     ///
     /// \param data The 8-bit integer to be written into the buffer.
-    void writeUint8(uint8_t data) { data_.push_back(data); }
+    void writeUint8(uint8_t data) {
+        ensureAllocated(size_ + 1);
+        buffer_[size_ ++] = data;
+    }
 
     /// \brief Write an unsigned 8-bit integer into the buffer.
     ///
@@ -377,8 +428,9 @@ public:
     /// \param data The 16-bit integer to be written into the buffer.
     void writeUint16(uint16_t data)
     {
-        data_.push_back(static_cast<uint8_t>((data & 0xff00U) >> 8));
-        data_.push_back(static_cast<uint8_t>(data & 0x00ffU));
+        ensureAllocated(size_ + sizeof(data));
+        buffer_[size_ ++] = static_cast<uint8_t>((data & 0xff00U) >> 8);
+        buffer_[size_ ++] = static_cast<uint8_t>(data & 0x00ffU);
     }
     /// \brief Write an unsigned 16-bit integer in host byte order at the
     /// specified position of the buffer in network byte order.
@@ -393,12 +445,12 @@ public:
     /// \param pos The beginning position in the buffer to write the data.
     void writeUint16At(uint16_t data, size_t pos)
     {
-        if (pos + sizeof(data) > data_.size()) {
+        if (pos + sizeof(data) > size_) {
             isc_throw(InvalidBufferPosition, "write at invalid position");
         }
 
-        data_[pos] = static_cast<uint8_t>((data & 0xff00U) >> 8);
-        data_[pos + 1] = static_cast<uint8_t>(data & 0x00ffU);
+        buffer_[pos] = static_cast<uint8_t>((data & 0xff00U) >> 8);
+        buffer_[pos + 1] = static_cast<uint8_t>(data & 0x00ffU);
     }
     /// \brief Write an unsigned 32-bit integer in host byte order
     /// into the buffer in network byte order.
@@ -406,10 +458,11 @@ public:
     /// \param data The 32-bit integer to be written into the buffer.
     void writeUint32(uint32_t data)
     {
-        data_.push_back(static_cast<uint8_t>((data & 0xff000000) >> 24));
-        data_.push_back(static_cast<uint8_t>((data & 0x00ff0000) >> 16));
-        data_.push_back(static_cast<uint8_t>((data & 0x0000ff00) >> 8));
-        data_.push_back(static_cast<uint8_t>(data & 0x000000ff));
+        ensureAllocated(size_ + sizeof(data));
+        buffer_[size_ ++] = static_cast<uint8_t>((data & 0xff000000) >> 24);
+        buffer_[size_ ++] = static_cast<uint8_t>((data & 0x00ff0000) >> 16);
+        buffer_[size_ ++] = static_cast<uint8_t>((data & 0x0000ff00) >> 8);
+        buffer_[size_ ++] = static_cast<uint8_t>(data & 0x000000ff);
     }
     /// \brief Copy an arbitrary length of data into the buffer.
     ///
@@ -419,13 +472,38 @@ public:
     /// \param len The length of the data in bytes.
     void writeData(const void *data, size_t len)
     {
-        const uint8_t* cp = static_cast<const uint8_t*>(data);
-        data_.insert(data_.end(), cp, cp + len);
+        ensureAllocated(size_ + len);
+        memcpy(buffer_ + size_, data, len);
+        size_ += len;
     }
     //@}
-    
+
 private:
-    std::vector<uint8_t> data_;
+    // The actual data
+    uint8_t* buffer_;
+    // How many bytes are used
+    size_t size_;
+    // How many bytes do we have preallocated (eg. the capacity)
+    size_t allocated_;
+    // Make sure at last needed_size bytes are allocated in the buffer
+    void ensureAllocated(size_t needed_size) {
+        if (allocated_ < needed_size) {
+            // Guess some bigger size
+            size_t new_size = (allocated_ == 0) ? 1024 : allocated_;
+            while (new_size < needed_size) {
+                new_size *= 2;
+            }
+            // Allocate bigger space
+            uint8_t* new_buffer_(static_cast<uint8_t*>(realloc(buffer_,
+                new_size)));
+            if (new_buffer_ == NULL) {
+                // If it fails, the original block is left intact by it
+                throw std::bad_alloc();
+            }
+            buffer_ = new_buffer_;
+            allocated_ = new_size;
+        }
+    }
 };
 
 /// \brief Pointer-like types pointing to \c InputBuffer or \c OutputBuffer

+ 49 - 0
src/lib/dns/tests/buffer_unittest.cc

@@ -190,4 +190,53 @@ TEST_F(BufferTest, outputBufferClear) {
     obuffer.clear();
     EXPECT_EQ(0, obuffer.getLength());
 }
+
+TEST_F(BufferTest, outputBufferCopy) {
+    obuffer.writeData(testdata, sizeof(testdata));
+
+    EXPECT_NO_THROW({
+        OutputBuffer copy(obuffer);
+        ASSERT_EQ(sizeof(testdata), copy.getLength());
+        for (int i = 0; i < sizeof(testdata); i ++) {
+            EXPECT_EQ(testdata[i], copy[i]);
+            if (i + 1 < sizeof(testdata)) {
+                obuffer.writeUint16At(0, i);
+            }
+            EXPECT_EQ(testdata[i], copy[i]);
+        }
+        obuffer.clear();
+        ASSERT_EQ(sizeof(testdata), copy.getLength());
+    });
+}
+
+TEST_F(BufferTest, outputBufferAssign) {
+    OutputBuffer another(0);
+    another.clear();
+    obuffer.writeData(testdata, sizeof(testdata));
+
+    EXPECT_NO_THROW({
+        another = obuffer;
+        ASSERT_EQ(sizeof(testdata), another.getLength());
+        for (int i = 0; i < sizeof(testdata); i ++) {
+            EXPECT_EQ(testdata[i], another[i]);
+            if (i + 1 < sizeof(testdata)) {
+                obuffer.writeUint16At(0, i);
+            }
+            EXPECT_EQ(testdata[i], another[i]);
+        }
+        obuffer.clear();
+        ASSERT_EQ(sizeof(testdata), another.getLength());
+    });
+}
+
+TEST_F(BufferTest, outputBufferZeroSize) {
+    // Some OSes might return NULL on malloc for 0 size, so check it works
+    EXPECT_NO_THROW({
+        OutputBuffer first(0);
+        OutputBuffer copy(first);
+        OutputBuffer second(0);
+        second = first;
+    });
+}
+
 }