1 // Scintilla source code edit control
2 /** @file Partitioning.h
3  ** Data structure used to partition an interval. Used for holding line start/end positions.
4  **/
5 // Copyright 1998-2007 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
7 
8 #ifndef PARTITIONING_H
9 #define PARTITIONING_H
10 
11 namespace Scintilla {
12 
13 /// A split vector of integers with a method for adding a value to all elements
14 /// in a range.
15 /// Used by the Partitioning class.
16 
17 template <typename T>
18 class SplitVectorWithRangeAdd : public SplitVector<T> {
19 public:
SplitVectorWithRangeAdd(ptrdiff_t growSize_)20 	explicit SplitVectorWithRangeAdd(ptrdiff_t growSize_) {
21 		this->SetGrowSize(growSize_);
22 		this->ReAllocate(growSize_);
23 	}
24 	// Deleted so SplitVectorWithRangeAdd objects can not be copied.
25 	SplitVectorWithRangeAdd(const SplitVectorWithRangeAdd &) = delete;
26 	SplitVectorWithRangeAdd(SplitVectorWithRangeAdd &&) = delete;
27 	void operator=(const SplitVectorWithRangeAdd &) = delete;
28 	void operator=(SplitVectorWithRangeAdd &&) = delete;
~SplitVectorWithRangeAdd()29 	~SplitVectorWithRangeAdd() {
30 	}
RangeAddDelta(ptrdiff_t start,ptrdiff_t end,T delta)31 	void RangeAddDelta(ptrdiff_t start, ptrdiff_t end, T delta) noexcept {
32 		// end is 1 past end, so end-start is number of elements to change
33 		ptrdiff_t i = 0;
34 		const ptrdiff_t rangeLength = end - start;
35 		ptrdiff_t range1Length = rangeLength;
36 		const ptrdiff_t part1Left = this->part1Length - start;
37 		if (range1Length > part1Left)
38 			range1Length = part1Left;
39 		while (i < range1Length) {
40 			this->body[start++] += delta;
41 			i++;
42 		}
43 		start += this->gapLength;
44 		while (i < rangeLength) {
45 			this->body[start++] += delta;
46 			i++;
47 		}
48 	}
49 };
50 
51 /// Divide an interval into multiple partitions.
52 /// Useful for breaking a document down into sections such as lines.
53 /// A 0 length interval has a single 0 length partition, numbered 0
54 /// If interval not 0 length then each partition non-zero length
55 /// When needed, positions after the interval are considered part of the last partition
56 /// but the end of the last partition can be found with PositionFromPartition(last+1).
57 
58 template <typename T>
59 class Partitioning {
60 private:
61 	// To avoid calculating all the partition positions whenever any text is inserted
62 	// there may be a step somewhere in the list.
63 	T stepPartition;
64 	T stepLength;
65 	std::unique_ptr<SplitVectorWithRangeAdd<T>> body;
66 
67 	// Move step forward
ApplyStep(T partitionUpTo)68 	void ApplyStep(T partitionUpTo) noexcept {
69 		if (stepLength != 0) {
70 			body->RangeAddDelta(stepPartition+1, partitionUpTo + 1, stepLength);
71 		}
72 		stepPartition = partitionUpTo;
73 		if (stepPartition >= body->Length()-1) {
74 			stepPartition = Partitions();
75 			stepLength = 0;
76 		}
77 	}
78 
79 	// Move step backward
BackStep(T partitionDownTo)80 	void BackStep(T partitionDownTo) noexcept {
81 		if (stepLength != 0) {
82 			body->RangeAddDelta(partitionDownTo+1, stepPartition+1, -stepLength);
83 		}
84 		stepPartition = partitionDownTo;
85 	}
86 
Allocate(ptrdiff_t growSize)87 	void Allocate(ptrdiff_t growSize) {
88 		body = std::make_unique<SplitVectorWithRangeAdd<T>>(growSize);
89 		stepPartition = 0;
90 		stepLength = 0;
91 		body->Insert(0, 0);	// This value stays 0 for ever
92 		body->Insert(1, 0);	// This is the end of the first partition and will be the start of the second
93 	}
94 
95 public:
Partitioning(int growSize)96 	explicit Partitioning(int growSize) : stepPartition(0), stepLength(0) {
97 		Allocate(growSize);
98 	}
99 
100 	// Deleted so Partitioning objects can not be copied.
101 	Partitioning(const Partitioning &) = delete;
102 	Partitioning(Partitioning &&) = delete;
103 	void operator=(const Partitioning &) = delete;
104 	void operator=(Partitioning &&) = delete;
105 
~Partitioning()106 	~Partitioning() {
107 	}
108 
Partitions()109 	T Partitions() const noexcept {
110 		return static_cast<T>(body->Length())-1;
111 	}
112 
Length()113 	T Length() const noexcept {
114 		return PositionFromPartition(Partitions());
115 	}
116 
InsertPartition(T partition,T pos)117 	void InsertPartition(T partition, T pos) {
118 		if (stepPartition < partition) {
119 			ApplyStep(partition);
120 		}
121 		body->Insert(partition, pos);
122 		stepPartition++;
123 	}
124 
InsertPartitions(T partition,const T * positions,size_t length)125 	void InsertPartitions(T partition, const T *positions, size_t length) {
126 		if (stepPartition < partition) {
127 			ApplyStep(partition);
128 		}
129 		body->InsertFromArray(partition, positions, 0, length);
130 		stepPartition += static_cast<T>(length);
131 	}
132 
InsertPartitionsWithCast(T partition,const ptrdiff_t * positions,size_t length)133 	void InsertPartitionsWithCast(T partition, const ptrdiff_t *positions, size_t length) {
134 		// Used for 64-bit builds when T is 32-bits
135 		if (stepPartition < partition) {
136 			ApplyStep(partition);
137 		}
138 		T *pInsertion = body->InsertEmpty(partition, length);
139 		for (size_t i = 0; i < length; i++) {
140 			pInsertion[i] = static_cast<T>(positions[i]);
141 		}
142 		stepPartition += static_cast<T>(length);
143 	}
144 
SetPartitionStartPosition(T partition,T pos)145 	void SetPartitionStartPosition(T partition, T pos) noexcept {
146 		ApplyStep(partition+1);
147 		if ((partition < 0) || (partition > body->Length())) {
148 			return;
149 		}
150 		body->SetValueAt(partition, pos);
151 	}
152 
InsertText(T partitionInsert,T delta)153 	void InsertText(T partitionInsert, T delta) noexcept {
154 		// Point all the partitions after the insertion point further along in the buffer
155 		if (stepLength != 0) {
156 			if (partitionInsert >= stepPartition) {
157 				// Fill in up to the new insertion point
158 				ApplyStep(partitionInsert);
159 				stepLength += delta;
160 			} else if (partitionInsert >= (stepPartition - body->Length() / 10)) {
161 				// Close to step but before so move step back
162 				BackStep(partitionInsert);
163 				stepLength += delta;
164 			} else {
165 				ApplyStep(Partitions());
166 				stepPartition = partitionInsert;
167 				stepLength = delta;
168 			}
169 		} else {
170 			stepPartition = partitionInsert;
171 			stepLength = delta;
172 		}
173 	}
174 
RemovePartition(T partition)175 	void RemovePartition(T partition) {
176 		if (partition > stepPartition) {
177 			ApplyStep(partition);
178 			stepPartition--;
179 		} else {
180 			stepPartition--;
181 		}
182 		body->Delete(partition);
183 	}
184 
PositionFromPartition(T partition)185 	T PositionFromPartition(T partition) const noexcept {
186 		PLATFORM_ASSERT(partition >= 0);
187 		PLATFORM_ASSERT(partition < body->Length());
188 		const ptrdiff_t lengthBody = body->Length();
189 		if ((partition < 0) || (partition >= lengthBody)) {
190 			return 0;
191 		}
192 		T pos = body->ValueAt(partition);
193 		if (partition > stepPartition)
194 			pos += stepLength;
195 		return pos;
196 	}
197 
198 	/// Return value in range [0 .. Partitions() - 1] even for arguments outside interval
PartitionFromPosition(T pos)199 	T PartitionFromPosition(T pos) const noexcept {
200 		if (body->Length() <= 1)
201 			return 0;
202 		if (pos >= (PositionFromPartition(Partitions())))
203 			return Partitions() - 1;
204 		T lower = 0;
205 		T upper = Partitions();
206 		do {
207 			const T middle = (upper + lower + 1) / 2; 	// Round high
208 			T posMiddle = body->ValueAt(middle);
209 			if (middle > stepPartition)
210 				posMiddle += stepLength;
211 			if (pos < posMiddle) {
212 				upper = middle - 1;
213 			} else {
214 				lower = middle;
215 			}
216 		} while (lower < upper);
217 		return lower;
218 	}
219 
DeleteAll()220 	void DeleteAll() {
221 		Allocate(body->GetGrowSize());
222 	}
223 
Check()224 	void Check() const {
225 #ifdef CHECK_CORRECTNESS
226 		if (Length() < 0) {
227 			throw std::runtime_error("Partitioning: Length can not be negative.");
228 		}
229 		if (Partitions() < 1) {
230 			throw std::runtime_error("Partitioning: Must always have 1 or more partitions.");
231 		}
232 		if (Length() == 0) {
233 			if ((PositionFromPartition(0) != 0) || (PositionFromPartition(1) != 0)) {
234 				throw std::runtime_error("Partitioning: Invalid empty partitioning.");
235 			}
236 		} else {
237 			// Positions should be a strictly ascending sequence
238 			for (T i = 0; i < Partitions(); i++) {
239 				const T pos = PositionFromPartition(i);
240 				const T posNext = PositionFromPartition(i+1);
241 				if (pos > posNext) {
242 					throw std::runtime_error("Partitioning: Negative partition.");
243 				} else if (pos == posNext) {
244 					throw std::runtime_error("Partitioning: Empty partition.");
245 				}
246 			}
247 		}
248 #endif
249 	}
250 
251 };
252 
253 
254 }
255 
256 #endif
257