Index: base/gap_buffer.h |
diff --git a/base/gap_buffer.h b/base/gap_buffer.h |
new file mode 100644 |
index 0000000000000000000000000000000000000000..a8a1dad91b121928c2214149df70c1210e2ab7b0 |
--- /dev/null |
+++ b/base/gap_buffer.h |
@@ -0,0 +1,219 @@ |
+// Copyright (c) 2010 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#ifndef BASE_GAP_BUFFER_H_ |
+#define BASE_GAP_BUFFER_H_ |
+#pragma once |
+ |
+#include <vector> |
+ |
+#include "base/basictypes.h" |
+#include "base/logging.h" |
+#include "base/scoped_ptr.h" |
+ |
+namespace base { |
+ |
+//////////////////////////////////////////////////////////////////////////////// |
+// |
+// This class implements a standard GapBuffer |
+// (http://en.wikipedia.org/wiki/Gap_buffer) |
+// |
+//////////////////////////////////////////////////////////////////////////////// |
+template<class T> |
+class GapBuffer { |
+ public: |
+ // Constructor. The initial size of the buffer can be specified in the |
+ // argument (defaults to 10). |
+ explicit GapBuffer(int size) { |
+ if (size < kMinResizeAmount) { |
+ size = kMinResizeAmount; |
+ } |
+ |
+ buffer_.reset(new T[size]); |
+ buffer_size_ = size; |
+ gap_start_ = 0; |
+ gap_end_ = buffer_size_; |
+ } |
+ |
+ // Constructor. Initializes the gap buffer with values from the input buffer. |
+ GapBuffer(const T* buffer, int buffer_size) { |
+ buffer_size_ = buffer_size + kMinResizeAmount; |
+ buffer_.reset(new T[buffer_size_]); |
+ gap_start_ = buffer_size; |
+ gap_end_ = buffer_size_; |
+ memmove(buffer_.get(), buffer, sizeof(T) * buffer_size); |
+ } |
+ |
+ // Moves the Gap left by "count" positions (until it hits the start of the |
+ // buffer). The gap size remains constant. Contents from the start of the gap |
+ // are moved to the end. |
+ void MoveGapLeft(int count) { |
+ if (buffer_.get() && gap_start_ > 0) { |
+ int new_gap_start = (gap_start_ >= count)? gap_start_ - count : 0; |
+ int num_elements = gap_start_ - new_gap_start; |
+ memmove(buffer_.get() + gap_end_ - num_elements, |
+ buffer_.get() + new_gap_start, sizeof(T) * num_elements); |
+ gap_start_ = new_gap_start; |
+ gap_end_ -= num_elements; |
+ } |
+ } |
+ |
+ // Moves the Gap right by "count" positions (until it hits the end of the |
+ // buffer). The gap size remains constant. Contents from the end of the gap |
+ // are moved to the start. |
+ void MoveGapRight(int count) { |
+ if (gap_end_ < buffer_size_) { |
+ int new_gap_end = (gap_end_ + count > buffer_size_)? buffer_size_ |
+ : gap_end_ + count; |
+ int num_elements = new_gap_end - gap_end_; |
+ memmove(buffer_.get() + gap_start_, buffer_.get() + gap_end_, |
+ sizeof(T) * num_elements); |
+ gap_start_ += num_elements; |
+ gap_end_ = new_gap_end; |
+ } |
+ } |
+ |
+ // Moves the Gap to the left until its just after the first occurrence of |
+ // "object". If object is not found it moves the gap all the way to the |
+ // beginning of the buffer. |
+ void MoveGapLeftTo(T object) { |
+ for (; gap_start_ > 0 && buffer_[gap_start_ - 1] != object;) { |
+ MoveGapLeftByOne(); |
+ } |
+ } |
+ |
+ // Moves the Gap to the right until its just after the first occurrence of |
+ // "object". If object is not found it moves the gap all the way to the end |
+ // of the buffer. |
+ void MoveGapRightTo(T object) { |
+ for (; gap_end_ < buffer_size_ && buffer_[gap_end_] != object;) { |
+ MoveGapRightByOne(); |
+ } |
+ // Move one more space to get after the occurrence on "object". |
+ MoveGapRight(1); |
+ } |
+ |
+ // Inserts one element in the gap at the beginning. Gap size decreases by one. |
+ // If gap is full, it resizes the buffer_ to create gap and then inserts. |
+ void InsertAtGapStart(T object) { |
+ if (gap_start_ < gap_end_) { |
+ buffer_[gap_start_] = object; |
+ gap_start_++; |
+ } else { |
+ ResizeBuffer(); |
+ InsertAtGapStart(object); |
+ } |
+ } |
+ |
+ // Removes an element from the beginning of the gap increasing the gap size |
+ // by one (unless the gap is already at the beginning of the buffer). Returns |
+ // a pointer to the element removed. |
+ const T* RemoveFromGapStart() { |
+ if (gap_start_ > 0) { |
+ gap_start_--; |
+ return buffer_.get() + gap_start_; |
+ } else { |
+ return NULL; |
+ } |
+ } |
+ |
+ // Removes an element from the end of the gap increasing the gap size |
+ // by one (unless the gap is already at the end of the buffer). Returns a |
+ // pointer to the element removed. |
+ const T* RemoveFromGapEnd() { |
+ if (gap_end_ < buffer_size_) { |
+ gap_end_++; |
+ return buffer_.get() + gap_end_ - 1; |
+ } else { |
+ return NULL; |
+ } |
+ } |
+ |
+ // Replaces the part of the buffer from "start" (inclusive) upto "end" with |
+ // "replacement". Resizes the gap appropriately. <start-end> should not |
+ // overlap with the gap. |
+ // FIXME: implement this. |
+ void Replace(int start, int end, T replacement) { |
+ NOTIMPLEMENTED(); |
+ } |
+ |
+ // Gets the contents of the buffer (removing the gap) in vector "contents". |
+ void GetContents(std::vector<T>* contents) { |
+ contents->clear(); |
+ int size = GetCurrentSize(); |
+ if (size) { |
+ int i, j; |
+ for (i = 0; i < gap_start_; i++) { |
+ (*contents).push_back(buffer_[i]); |
+ } |
+ for (j = gap_end_; j < buffer_size_; j++, i++) { |
+ (*contents).push_back(buffer_[j]); |
+ } |
+ } |
+ } |
+ |
+ // Gets the current number of elements in the buffer. |
+ int GetCurrentSize() { |
+ return buffer_size_ - (gap_end_ - gap_start_); |
+ } |
+ |
+ // Gets the size of the underlying buffer. |
+ int GetBufferSize() { |
+ return buffer_size_; |
+ } |
+ |
+ // Gets the gap_start_ index (essentially the current cursor position). |
+ int GetGapStart() { |
+ return gap_start_; |
+ } |
+ |
+ private: |
+ // Increases the size of the buffer by 10% of current size. Unsafe.. assumes |
+ // that the buffer is already allocated. Should not be invoked for |
+ // un-initialized buffer. |
+ // FIXME: explore more efficient resizing. |
+ void ResizeBuffer() { |
+ int size_increment = (buffer_size_ / 10.0 < kMinResizeAmount)? |
+ kMinResizeAmount : buffer_size_ / 10.0; |
+ int new_size = buffer_size_ + size_increment; |
+ T* new_buffer = new T[new_size]; |
+ memmove(new_buffer, buffer_.get(), sizeof(T) * gap_start_); |
+ memmove(new_buffer + gap_start_ + new_size, buffer_.get() + gap_end_, |
+ sizeof(T) * (buffer_size_ - gap_end_)); |
+ gap_end_ = new_size - (buffer_size_ - gap_end_); |
+ buffer_size_ = new_size; |
+ buffer_.reset(new_buffer); |
+ } |
+ |
+ // Internal helper function. Should not be used on its own as it doesnot do |
+ // any sanity checks. Use MoveGapLeft() or MoveGapLeftTo() instead. |
+ void MoveGapLeftByOne() { |
+ gap_start_--; |
+ gap_end_--; |
+ buffer_[gap_end_] = buffer_[gap_start_]; |
+ } |
+ |
+ // Internal helper function. Should not be used on its own as it doesnot do |
+ // any sanity checks. Use MoveGapRight() or MoveGapRightTo() instead. |
+ void MoveGapRightByOne() { |
+ buffer_[gap_start_] = buffer_[gap_end_]; |
+ gap_start_++; |
+ gap_end_++; |
+ } |
+ |
+ scoped_array<T> buffer_; |
+ int buffer_size_; |
+ int gap_start_; |
+ int gap_end_; |
+ |
+ // In case of resizing the buffer, we will increase the length at least by |
+ // kMinResizeAmount to avoid frequent resizing. |
+ static const int kMinResizeAmount = 10; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(GapBuffer); |
+}; |
+ |
+} // namespace base |
+ |
+#endif // BASE_GAP_BUFFER_H_ |