OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #ifndef BASE_GAP_BUFFER_H_ |
| 6 #define BASE_GAP_BUFFER_H_ |
| 7 #pragma once |
| 8 |
| 9 #include <vector> |
| 10 |
| 11 #include "base/basictypes.h" |
| 12 #include "base/logging.h" |
| 13 #include "base/scoped_ptr.h" |
| 14 |
| 15 namespace base { |
| 16 |
| 17 //////////////////////////////////////////////////////////////////////////////// |
| 18 // |
| 19 // This class implements a standard GapBuffer |
| 20 // (http://en.wikipedia.org/wiki/Gap_buffer) |
| 21 // |
| 22 //////////////////////////////////////////////////////////////////////////////// |
| 23 template<class T> |
| 24 class GapBuffer { |
| 25 public: |
| 26 // Constructor. The initial size of the buffer can be specified in the |
| 27 // argument (defaults to 10). |
| 28 explicit GapBuffer(int size) { |
| 29 if (size < kMinResizeAmount) { |
| 30 size = kMinResizeAmount; |
| 31 } |
| 32 |
| 33 buffer_.reset(new T[size]); |
| 34 buffer_size_ = size; |
| 35 gap_start_ = 0; |
| 36 gap_end_ = buffer_size_; |
| 37 } |
| 38 |
| 39 // Constructor. Initializes the gap buffer with values from the input buffer. |
| 40 GapBuffer(const T* buffer, int buffer_size) { |
| 41 buffer_size_ = buffer_size + kMinResizeAmount; |
| 42 buffer_.reset(new T[buffer_size_]); |
| 43 gap_start_ = buffer_size; |
| 44 gap_end_ = buffer_size_; |
| 45 memmove(buffer_.get(), buffer, sizeof(T) * buffer_size); |
| 46 } |
| 47 |
| 48 // Moves the Gap left by "count" positions (until it hits the start of the |
| 49 // buffer). The gap size remains constant. Contents from the start of the gap |
| 50 // are moved to the end. |
| 51 void MoveGapLeft(int count) { |
| 52 if (buffer_.get() && gap_start_ > 0) { |
| 53 int new_gap_start = (gap_start_ >= count)? gap_start_ - count : 0; |
| 54 int num_elements = gap_start_ - new_gap_start; |
| 55 memmove(buffer_.get() + gap_end_ - num_elements, |
| 56 buffer_.get() + new_gap_start, sizeof(T) * num_elements); |
| 57 gap_start_ = new_gap_start; |
| 58 gap_end_ -= num_elements; |
| 59 } |
| 60 } |
| 61 |
| 62 // Moves the Gap right by "count" positions (until it hits the end of the |
| 63 // buffer). The gap size remains constant. Contents from the end of the gap |
| 64 // are moved to the start. |
| 65 void MoveGapRight(int count) { |
| 66 if (gap_end_ < buffer_size_) { |
| 67 int new_gap_end = (gap_end_ + count > buffer_size_)? buffer_size_ |
| 68 : gap_end_ + count; |
| 69 int num_elements = new_gap_end - gap_end_; |
| 70 memmove(buffer_.get() + gap_start_, buffer_.get() + gap_end_, |
| 71 sizeof(T) * num_elements); |
| 72 gap_start_ += num_elements; |
| 73 gap_end_ = new_gap_end; |
| 74 } |
| 75 } |
| 76 |
| 77 // Moves the Gap to the left until its just after the first occurrence of |
| 78 // "object". If object is not found it moves the gap all the way to the |
| 79 // beginning of the buffer. |
| 80 void MoveGapLeftTo(T object) { |
| 81 for (; gap_start_ > 0 && buffer_[gap_start_ - 1] != object;) { |
| 82 MoveGapLeftByOne(); |
| 83 } |
| 84 } |
| 85 |
| 86 // Moves the Gap to the right until its just after the first occurrence of |
| 87 // "object". If object is not found it moves the gap all the way to the end |
| 88 // of the buffer. |
| 89 void MoveGapRightTo(T object) { |
| 90 for (; gap_end_ < buffer_size_ && buffer_[gap_end_] != object;) { |
| 91 MoveGapRightByOne(); |
| 92 } |
| 93 // Move one more space to get after the occurrence on "object". |
| 94 MoveGapRight(1); |
| 95 } |
| 96 |
| 97 // Inserts one element in the gap at the beginning. Gap size decreases by one. |
| 98 // If gap is full, it resizes the buffer_ to create gap and then inserts. |
| 99 void InsertAtGapStart(T object) { |
| 100 if (gap_start_ < gap_end_) { |
| 101 buffer_[gap_start_] = object; |
| 102 gap_start_++; |
| 103 } else { |
| 104 ResizeBuffer(); |
| 105 InsertAtGapStart(object); |
| 106 } |
| 107 } |
| 108 |
| 109 // Removes an element from the beginning of the gap increasing the gap size |
| 110 // by one (unless the gap is already at the beginning of the buffer). Returns |
| 111 // a pointer to the element removed. |
| 112 const T* RemoveFromGapStart() { |
| 113 if (gap_start_ > 0) { |
| 114 gap_start_--; |
| 115 return buffer_.get() + gap_start_; |
| 116 } else { |
| 117 return NULL; |
| 118 } |
| 119 } |
| 120 |
| 121 // Removes an element from the end of the gap increasing the gap size |
| 122 // by one (unless the gap is already at the end of the buffer). Returns a |
| 123 // pointer to the element removed. |
| 124 const T* RemoveFromGapEnd() { |
| 125 if (gap_end_ < buffer_size_) { |
| 126 gap_end_++; |
| 127 return buffer_.get() + gap_end_ - 1; |
| 128 } else { |
| 129 return NULL; |
| 130 } |
| 131 } |
| 132 |
| 133 // Replaces the part of the buffer from "start" (inclusive) upto "end" with |
| 134 // "replacement". Resizes the gap appropriately. <start-end> should not |
| 135 // overlap with the gap. |
| 136 // FIXME: implement this. |
| 137 void Replace(int start, int end, T replacement) { |
| 138 NOTIMPLEMENTED(); |
| 139 } |
| 140 |
| 141 // Gets the contents of the buffer (removing the gap) in vector "contents". |
| 142 void GetContents(std::vector<T>* contents) { |
| 143 contents->clear(); |
| 144 int size = GetCurrentSize(); |
| 145 if (size) { |
| 146 int i, j; |
| 147 for (i = 0; i < gap_start_; i++) { |
| 148 (*contents).push_back(buffer_[i]); |
| 149 } |
| 150 for (j = gap_end_; j < buffer_size_; j++, i++) { |
| 151 (*contents).push_back(buffer_[j]); |
| 152 } |
| 153 } |
| 154 } |
| 155 |
| 156 // Gets the current number of elements in the buffer. |
| 157 int GetCurrentSize() { |
| 158 return buffer_size_ - (gap_end_ - gap_start_); |
| 159 } |
| 160 |
| 161 // Gets the size of the underlying buffer. |
| 162 int GetBufferSize() { |
| 163 return buffer_size_; |
| 164 } |
| 165 |
| 166 // Gets the gap_start_ index (essentially the current cursor position). |
| 167 int GetGapStart() { |
| 168 return gap_start_; |
| 169 } |
| 170 |
| 171 private: |
| 172 // Increases the size of the buffer by 10% of current size. Unsafe.. assumes |
| 173 // that the buffer is already allocated. Should not be invoked for |
| 174 // un-initialized buffer. |
| 175 // FIXME: explore more efficient resizing. |
| 176 void ResizeBuffer() { |
| 177 int size_increment = (buffer_size_ / 10.0 < kMinResizeAmount)? |
| 178 kMinResizeAmount : buffer_size_ / 10.0; |
| 179 int new_size = buffer_size_ + size_increment; |
| 180 T* new_buffer = new T[new_size]; |
| 181 memmove(new_buffer, buffer_.get(), sizeof(T) * gap_start_); |
| 182 memmove(new_buffer + gap_start_ + new_size, buffer_.get() + gap_end_, |
| 183 sizeof(T) * (buffer_size_ - gap_end_)); |
| 184 gap_end_ = new_size - (buffer_size_ - gap_end_); |
| 185 buffer_size_ = new_size; |
| 186 buffer_.reset(new_buffer); |
| 187 } |
| 188 |
| 189 // Internal helper function. Should not be used on its own as it doesnot do |
| 190 // any sanity checks. Use MoveGapLeft() or MoveGapLeftTo() instead. |
| 191 void MoveGapLeftByOne() { |
| 192 gap_start_--; |
| 193 gap_end_--; |
| 194 buffer_[gap_end_] = buffer_[gap_start_]; |
| 195 } |
| 196 |
| 197 // Internal helper function. Should not be used on its own as it doesnot do |
| 198 // any sanity checks. Use MoveGapRight() or MoveGapRightTo() instead. |
| 199 void MoveGapRightByOne() { |
| 200 buffer_[gap_start_] = buffer_[gap_end_]; |
| 201 gap_start_++; |
| 202 gap_end_++; |
| 203 } |
| 204 |
| 205 scoped_array<T> buffer_; |
| 206 int buffer_size_; |
| 207 int gap_start_; |
| 208 int gap_end_; |
| 209 |
| 210 // In case of resizing the buffer, we will increase the length at least by |
| 211 // kMinResizeAmount to avoid frequent resizing. |
| 212 static const int kMinResizeAmount = 10; |
| 213 |
| 214 DISALLOW_COPY_AND_ASSIGN(GapBuffer); |
| 215 }; |
| 216 |
| 217 } // namespace base |
| 218 |
| 219 #endif // BASE_GAP_BUFFER_H_ |
OLD | NEW |