1 // Scintilla source code edit control
2 /** @file SplitVector.h
3 ** Main data structure for holding arrays that handle insertions
4 ** and deletions efficiently.
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.
18 T empty
; /// Returned as the result of out-of-bounds access.
20 ptrdiff_t part1Length
;
21 ptrdiff_t gapLength
; /// invariant: gapLength == body.size() - lengthBody
24 /// Move the gap to a particular position so that insertion and
25 /// deletion at that point will not require much copying and
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
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
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))
52 ReAllocate(body
.size() + insertionLength
+ growSize
);
66 /// Construct a split buffer.
67 SplitVector() : empty() {
71 // Deleted so SplitVector objects can not be copied.
72 SplitVector(const SplitVector
&) = delete;
73 void operator=(const SplitVector
&) = delete;
78 ptrdiff_t GetGrowSize() const {
82 void SetGrowSize(ptrdiff_t 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
) {
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
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
) {
112 return body
[position
];
115 if (position
>= lengthBody
) {
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);
133 body
[position
] = std::move(v
);
136 PLATFORM_ASSERT(position
< lengthBody
);
137 if (position
>= lengthBody
) {
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
];
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
];
164 return body
[gapLength
+ position
];
168 /// Retrieve the length of the buffer.
169 ptrdiff_t Length() const {
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
)) {
182 body
[part1Length
] = std::move(v
);
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
)) {
196 RoomFor(insertLength
);
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
)) {
214 RoomFor(insertLength
);
216 for (ptrdiff_t elem
= part1Length
; elem
< part1Length
+ insertLength
; elem
++) {
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
)) {
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
)) {
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
)) {
266 if ((position
== 0) && (deleteLength
== lengthBody
)) {
267 // Full deallocation returns storage and is faster
269 body
.shrink_to_fit();
271 } else if (deleteLength
> 0) {
273 lengthBody
-= deleteLength
;
274 gapLength
+= deleteLength
;
278 /// Delete all the buffer contents.
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.
305 body
[lengthBody
] = std::move(emptyOne
);
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.
316 return body
.data() + position
+ gapLength
;
318 return body
.data() + position
;
321 return body
.data() + position
+ gapLength
;
325 /// Return the position of the gap within the buffer.
326 ptrdiff_t GapPosition() const {