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 <[email protected]> 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