Scintilla 4.0.3
[TortoiseGit.git] / ext / scintilla / src / SplitVector.h
blobffcb5332b6d46ca5cb2f1df2ee6c878791af4cb2
1 // Scintilla source code edit control
2 /** @file SplitVector.h
3 ** Main data structure for holding arrays that handle insertions
4 ** and deletions efficiently.
5 **/
6 // Copyright 1998-2007 by Neil Hodgson <neilh@scintilla.org>
7 // The License.txt file describes the conditions under which this software may be distributed.
9 #ifndef SPLITVECTOR_H
10 #define SPLITVECTOR_H
12 namespace Scintilla {
14 template <typename T>
15 class SplitVector {
16 protected:
17 std::vector<T> body;
18 T empty; /// Returned as the result of out-of-bounds access.
19 ptrdiff_t lengthBody;
20 ptrdiff_t part1Length;
21 ptrdiff_t gapLength; /// invariant: gapLength == body.size() - lengthBody
22 ptrdiff_t growSize;
24 /// Move the gap to a particular position so that insertion and
25 /// deletion at that point will not require much copying and
26 /// hence be fast.
27 void GapTo(ptrdiff_t position) {
28 if (position != part1Length) {
29 if (position < part1Length) {
30 // Moving the gap towards start so moving elements towards end
31 std::move_backward(
32 body.data() + position,
33 body.data() + part1Length,
34 body.data() + gapLength + part1Length);
35 } else { // position > part1Length
36 // Moving the gap towards end so moving elements towards start
37 std::move(
38 body.data() + part1Length + gapLength,
39 body.data() + gapLength + position,
40 body.data() + part1Length);
42 part1Length = position;
46 /// Check that there is room in the buffer for an insertion,
47 /// reallocating if more space needed.
48 void RoomFor(ptrdiff_t insertionLength) {
49 if (gapLength <= insertionLength) {
50 while (growSize < static_cast<ptrdiff_t>(body.size() / 6))
51 growSize *= 2;
52 ReAllocate(body.size() + insertionLength + growSize);
56 void Init() {
57 body.clear();
58 body.shrink_to_fit();
59 growSize = 8;
60 lengthBody = 0;
61 part1Length = 0;
62 gapLength = 0;
65 public:
66 /// Construct a split buffer.
67 SplitVector() : empty() {
68 Init();
71 // Deleted so SplitVector objects can not be copied.
72 SplitVector(const SplitVector &) = delete;
73 void operator=(const SplitVector &) = delete;
75 ~SplitVector() {
78 ptrdiff_t GetGrowSize() const {
79 return growSize;
82 void SetGrowSize(ptrdiff_t growSize_) {
83 growSize = growSize_;
86 /// Reallocate the storage for the buffer to be newSize and
87 /// copy exisiting contents to the new buffer.
88 /// Must not be used to decrease the size of the buffer.
89 void ReAllocate(ptrdiff_t newSize) {
90 if (newSize < 0)
91 throw std::runtime_error("SplitVector::ReAllocate: negative size.");
93 if (newSize > static_cast<ptrdiff_t>(body.size())) {
94 // Move the gap to the end
95 GapTo(lengthBody);
96 gapLength += newSize - static_cast<ptrdiff_t>(body.size());
97 // RoomFor implements a growth strategy but so does vector::resize so
98 // ensure vector::resize allocates exactly the amount wanted by
99 // calling reserve first.
100 body.reserve(newSize);
101 body.resize(newSize);
105 /// Retrieve the element at a particular position.
106 /// Retrieving positions outside the range of the buffer returns empty or 0.
107 const T& ValueAt(ptrdiff_t position) const {
108 if (position < part1Length) {
109 if (position < 0) {
110 return empty;
111 } else {
112 return body[position];
114 } else {
115 if (position >= lengthBody) {
116 return empty;
117 } else {
118 return body[gapLength + position];
123 /// Set the element at a particular position.
124 /// Setting positions outside the range of the buffer performs no assignment
125 /// but asserts in debug builds.
126 template <typename ParamType>
127 void SetValueAt(ptrdiff_t position, ParamType&& v) {
128 if (position < part1Length) {
129 PLATFORM_ASSERT(position >= 0);
130 if (position < 0) {
132 } else {
133 body[position] = std::move(v);
135 } else {
136 PLATFORM_ASSERT(position < lengthBody);
137 if (position >= lengthBody) {
139 } else {
140 body[gapLength + position] = std::move(v);
145 /// Retrieve the element at a particular position.
146 /// The position must be within bounds or an assertion is triggered.
147 const T &operator[](ptrdiff_t position) const {
148 PLATFORM_ASSERT(position >= 0 && position < lengthBody);
149 if (position < part1Length) {
150 return body[position];
151 } else {
152 return body[gapLength + position];
156 /// Retrieve reference to the element at a particular position.
157 /// This, instead of the const variant, can be used to mutate in-place.
158 /// The position must be within bounds or an assertion is triggered.
159 T &operator[](ptrdiff_t position) {
160 PLATFORM_ASSERT(position >= 0 && position < lengthBody);
161 if (position < part1Length) {
162 return body[position];
163 } else {
164 return body[gapLength + position];
168 /// Retrieve the length of the buffer.
169 ptrdiff_t Length() const {
170 return lengthBody;
173 /// Insert a single value into the buffer.
174 /// Inserting at positions outside the current range fails.
175 void Insert(ptrdiff_t position, T v) {
176 PLATFORM_ASSERT((position >= 0) && (position <= lengthBody));
177 if ((position < 0) || (position > lengthBody)) {
178 return;
180 RoomFor(1);
181 GapTo(position);
182 body[part1Length] = std::move(v);
183 lengthBody++;
184 part1Length++;
185 gapLength--;
188 /// Insert a number of elements into the buffer setting their value.
189 /// Inserting at positions outside the current range fails.
190 void InsertValue(ptrdiff_t position, ptrdiff_t insertLength, T v) {
191 PLATFORM_ASSERT((position >= 0) && (position <= lengthBody));
192 if (insertLength > 0) {
193 if ((position < 0) || (position > lengthBody)) {
194 return;
196 RoomFor(insertLength);
197 GapTo(position);
198 std::fill(body.data() + part1Length, body.data() + part1Length + insertLength, v);
199 lengthBody += insertLength;
200 part1Length += insertLength;
201 gapLength -= insertLength;
205 /// Add some new empty elements.
206 /// InsertValue is good for value objects but not for unique_ptr objects
207 /// since they can only be moved from once.
208 void InsertEmpty(ptrdiff_t position, ptrdiff_t insertLength) {
209 PLATFORM_ASSERT((position >= 0) && (position <= lengthBody));
210 if (insertLength > 0) {
211 if ((position < 0) || (position > lengthBody)) {
212 return;
214 RoomFor(insertLength);
215 GapTo(position);
216 for (ptrdiff_t elem = part1Length; elem < part1Length + insertLength; elem++) {
217 T emptyOne = {};
218 body[elem] = std::move(emptyOne);
220 lengthBody += insertLength;
221 part1Length += insertLength;
222 gapLength -= insertLength;
226 /// Ensure at least length elements allocated,
227 /// appending zero valued elements if needed.
228 void EnsureLength(ptrdiff_t wantedLength) {
229 if (Length() < wantedLength) {
230 InsertEmpty(Length(), wantedLength - Length());
234 /// Insert text into the buffer from an array.
235 void InsertFromArray(ptrdiff_t positionToInsert, const T s[], ptrdiff_t positionFrom, ptrdiff_t insertLength) {
236 PLATFORM_ASSERT((positionToInsert >= 0) && (positionToInsert <= lengthBody));
237 if (insertLength > 0) {
238 if ((positionToInsert < 0) || (positionToInsert > lengthBody)) {
239 return;
241 RoomFor(insertLength);
242 GapTo(positionToInsert);
243 std::copy(s + positionFrom, s + positionFrom + insertLength, body.data() + part1Length);
244 lengthBody += insertLength;
245 part1Length += insertLength;
246 gapLength -= insertLength;
250 /// Delete one element from the buffer.
251 void Delete(ptrdiff_t position) {
252 PLATFORM_ASSERT((position >= 0) && (position < lengthBody));
253 if ((position < 0) || (position >= lengthBody)) {
254 return;
256 DeleteRange(position, 1);
259 /// Delete a range from the buffer.
260 /// Deleting positions outside the current range fails.
261 void DeleteRange(ptrdiff_t position, ptrdiff_t deleteLength) {
262 PLATFORM_ASSERT((position >= 0) && (position + deleteLength <= lengthBody));
263 if ((position < 0) || ((position + deleteLength) > lengthBody)) {
264 return;
266 if ((position == 0) && (deleteLength == lengthBody)) {
267 // Full deallocation returns storage and is faster
268 body.clear();
269 body.shrink_to_fit();
270 Init();
271 } else if (deleteLength > 0) {
272 GapTo(position);
273 lengthBody -= deleteLength;
274 gapLength += deleteLength;
278 /// Delete all the buffer contents.
279 void DeleteAll() {
280 DeleteRange(0, lengthBody);
283 /// Retrieve a range of elements into an array
284 void GetRange(T *buffer, ptrdiff_t position, ptrdiff_t retrieveLength) const {
285 // Split into up to 2 ranges, before and after the split then use memcpy on each.
286 ptrdiff_t range1Length = 0;
287 if (position < part1Length) {
288 const ptrdiff_t part1AfterPosition = part1Length - position;
289 range1Length = retrieveLength;
290 if (range1Length > part1AfterPosition)
291 range1Length = part1AfterPosition;
293 std::copy(body.data() + position, body.data() + position + range1Length, buffer);
294 buffer += range1Length;
295 position = position + range1Length + gapLength;
296 ptrdiff_t range2Length = retrieveLength - range1Length;
297 std::copy(body.data() + position, body.data() + position + range2Length, buffer);
300 /// Compact the buffer and return a pointer to the first element.
301 T *BufferPointer() {
302 RoomFor(1);
303 GapTo(lengthBody);
304 T emptyOne = {};
305 body[lengthBody] = std::move(emptyOne);
306 return body.data();
309 /// Return a pointer to a range of elements, first rearranging the buffer if
310 /// needed to make that range contiguous.
311 T *RangePointer(ptrdiff_t position, ptrdiff_t rangeLength) {
312 if (position < part1Length) {
313 if ((position + rangeLength) > part1Length) {
314 // Range overlaps gap, so move gap to start of range.
315 GapTo(position);
316 return body.data() + position + gapLength;
317 } else {
318 return body.data() + position;
320 } else {
321 return body.data() + position + gapLength;
325 /// Return the position of the gap within the buffer.
326 ptrdiff_t GapPosition() const {
327 return part1Length;
333 #endif