1 // Scintilla source code edit control 2 /** @file SparseVector.h 3 ** Hold data sparsely associated with elements in a range. 4 **/ 5 // Copyright 2016 by Neil Hodgson <[email protected]> 6 // The License.txt file describes the conditions under which this software may be distributed. 7 8 #ifndef SPARSEVECTOR_H 9 #define SPARSEVECTOR_H 10 11 namespace Scintilla { 12 13 // SparseVector is similar to RunStyles but is more efficient for cases where values occur 14 // for one position instead of over a range of positions. 15 // There are always elements at the start and end, so the element type should have 16 // a reasonable empty value that will cause no problems. 17 // The element type should have a noexcept default constructor as that allows methods to 18 // be noexcept. 19 template <typename T> 20 class SparseVector { 21 private: 22 std::unique_ptr<Partitioning<Sci::Position>> starts; 23 std::unique_ptr<SplitVector<T>> values; 24 T empty; // Return from ValueAt when no element at a position. ClearValue(Sci::Position partition)25 void ClearValue(Sci::Position partition) noexcept { 26 values->SetValueAt(partition, T()); 27 } 28 public: SparseVector()29 SparseVector() : empty() { 30 starts = std::make_unique<Partitioning<Sci::Position>>(8); 31 values = std::make_unique<SplitVector<T>>(); 32 values->InsertEmpty(0, 2); 33 } 34 // Deleted so SparseVector objects can not be copied. 35 SparseVector(const SparseVector &) = delete; 36 SparseVector(SparseVector &&) = delete; 37 void operator=(const SparseVector &) = delete; 38 void operator=(SparseVector &&) = delete; ~SparseVector()39 ~SparseVector() { 40 starts.reset(); 41 // starts dead here but not used by ClearValue. 42 for (Sci::Position part = 0; part < values->Length(); part++) { 43 ClearValue(part); 44 } 45 values.reset(); 46 } Length()47 Sci::Position Length() const noexcept { 48 return starts->Length(); 49 } Elements()50 Sci::Position Elements() const noexcept { 51 return starts->Partitions(); 52 } PositionOfElement(Sci::Position element)53 Sci::Position PositionOfElement(Sci::Position element) const noexcept { 54 return starts->PositionFromPartition(element); 55 } ElementFromPosition(Sci::Position position)56 Sci::Position ElementFromPosition(Sci::Position position) const noexcept { 57 if (position < Length()) { 58 return starts->PartitionFromPosition(position); 59 } else { 60 return starts->Partitions(); 61 } 62 } ValueAt(Sci::Position position)63 const T& ValueAt(Sci::Position position) const noexcept { 64 assert(position <= Length()); 65 const Sci::Position partition = ElementFromPosition(position); 66 const Sci::Position startPartition = starts->PositionFromPartition(partition); 67 if (startPartition == position) { 68 return values->ValueAt(partition); 69 } else { 70 return empty; 71 } 72 } 73 template <typename ParamType> SetValueAt(Sci::Position position,ParamType && value)74 void SetValueAt(Sci::Position position, ParamType &&value) { 75 assert(position <= Length()); 76 const Sci::Position partition = ElementFromPosition(position); 77 const Sci::Position startPartition = starts->PositionFromPartition(partition); 78 if (value == T()) { 79 // Setting the empty value is equivalent to deleting the position 80 if (position == 0) { 81 ClearValue(partition); 82 } else if (position == startPartition) { 83 // Currently an element at this position, so remove 84 ClearValue(partition); 85 starts->RemovePartition(partition); 86 values->Delete(partition); 87 } 88 // Else element remains empty 89 } else { 90 if (position == startPartition) { 91 // Already a value at this position, so replace 92 ClearValue(partition); 93 values->SetValueAt(partition, std::forward<ParamType>(value)); 94 } else { 95 // Insert a new element 96 starts->InsertPartition(partition + 1, position); 97 values->Insert(partition + 1, std::forward<ParamType>(value)); 98 } 99 } 100 } InsertSpace(Sci::Position position,Sci::Position insertLength)101 void InsertSpace(Sci::Position position, Sci::Position insertLength) { 102 assert(position <= Length()); 103 const Sci::Position partition = starts->PartitionFromPosition(position); 104 const Sci::Position startPartition = starts->PositionFromPartition(partition); 105 if (startPartition == position) { 106 const bool positionOccupied = values->ValueAt(partition) != T(); 107 // Inserting at start of run so make previous longer 108 if (partition == 0) { 109 // Inserting at start of document so ensure start empty 110 if (positionOccupied) { 111 starts->InsertPartition(1, 0); 112 values->InsertEmpty(0, 1); 113 } 114 starts->InsertText(partition, insertLength); 115 } else { 116 if (positionOccupied) { 117 starts->InsertText(partition - 1, insertLength); 118 } else { 119 // Insert at end of run so do not extend style 120 starts->InsertText(partition, insertLength); 121 } 122 } 123 } else { 124 starts->InsertText(partition, insertLength); 125 } 126 } DeletePosition(Sci::Position position)127 void DeletePosition(Sci::Position position) { 128 assert(position < Length()); 129 Sci::Position partition = starts->PartitionFromPosition(position); 130 const Sci::Position startPartition = starts->PositionFromPartition(partition); 131 if (startPartition == position) { 132 if (partition == 0) { 133 ClearValue(0); 134 if (starts->PositionFromPartition(1) == 1) { 135 // Removing all space of first partition, so remove next partition 136 // and move value if not last 137 if (Elements() > 1) { 138 starts->RemovePartition(partition + 1); 139 values->Delete(partition); 140 } 141 } 142 } else if (partition == starts->Partitions()) { 143 // This should not be possible 144 ClearValue(partition); 145 throw std::runtime_error("SparseVector: deleting end partition."); 146 } else { 147 ClearValue(partition); 148 starts->RemovePartition(partition); 149 values->Delete(partition); 150 // Its the previous partition now that gets smaller 151 partition--; 152 } 153 } 154 starts->InsertText(partition, -1); 155 Check(); 156 } DeleteRange(Sci::Position position,Sci::Position deleteLength)157 void DeleteRange(Sci::Position position, Sci::Position deleteLength) { 158 // For now, delete elements in range - may want to leave value at start 159 // or combine onto position. 160 if (position > Length() || (deleteLength == 0)) { 161 return; 162 } 163 const Sci::Position positionEnd = position + deleteLength; 164 assert(positionEnd <= Length()); 165 if (position == 0) { 166 // Remove all partitions in range, moving values to start 167 while ((Elements() > 1) && (starts->PositionFromPartition(1) <= deleteLength)) { 168 starts->RemovePartition(1); 169 values->Delete(0); 170 } 171 starts->InsertText(0, -deleteLength); 172 if (Length() == 0) { 173 ClearValue(0); 174 } 175 } else { 176 const Sci::Position partition = starts->PartitionFromPosition(position); 177 const bool atPartitionStart = position == starts->PositionFromPartition(partition); 178 const Sci::Position partitionDelete = partition + (atPartitionStart ? 0 : 1); 179 assert(partitionDelete > 0); 180 for (;;) { 181 const Sci::Position positionAtIndex = starts->PositionFromPartition(partitionDelete); 182 assert(position <= positionAtIndex); 183 if (positionAtIndex >= positionEnd) { 184 break; 185 } 186 assert(partitionDelete <= Elements()); 187 starts->RemovePartition(partitionDelete); 188 values->Delete(partitionDelete); 189 } 190 starts->InsertText(partition - (atPartitionStart ? 1 : 0), -deleteLength); 191 } 192 Check(); 193 } IndexAfter(Sci::Position position)194 Sci::Position IndexAfter(Sci::Position position) const noexcept { 195 assert(position < Length()); 196 if (position < 0) 197 return 0; 198 const Sci::Position partition = starts->PartitionFromPosition(position); 199 return partition + 1; 200 } Check()201 void Check() const { 202 #ifdef CHECK_CORRECTNESS 203 starts->Check(); 204 if (starts->Partitions() != values->Length() - 1) { 205 throw std::runtime_error("SparseVector: Partitions and values different lengths."); 206 } 207 #endif 208 } 209 }; 210 211 } 212 213 #endif 214