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