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