1 // Scintilla source code edit control
2 /** @file SparseVector.h
3 ** Hold data sparsely associated with elements in a range.
5 // Copyright 2016 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
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.
18 std::unique_ptr
<Partitioning
<int>> starts
;
19 std::unique_ptr
<SplitVector
<T
>> values
;
21 // Deleted so SparseVector objects can not be copied.
22 SparseVector(const SparseVector
&) = delete;
23 void operator=(const SparseVector
&) = delete;
24 void ClearValue(int partition
) {
25 values
->SetValueAt(partition
, T());
28 SparseVector() : empty() {
29 starts
.reset(new Partitioning
<int>(8));
30 values
.reset(new SplitVector
<T
>());
31 values
->InsertEmpty(0, 2);
35 // starts dead here but not used by ClearValue.
36 for (int part
= 0; part
< values
->Length(); part
++) {
42 return starts
->PositionFromPartition(starts
->Partitions());
44 int Elements() const {
45 return starts
->Partitions();
47 int PositionOfElement(int element
) const {
48 return starts
->PositionFromPartition(element
);
50 const T
& ValueAt(int position
) const {
51 assert(position
< Length());
52 const int partition
= starts
->PartitionFromPosition(position
);
53 const int startPartition
= starts
->PositionFromPartition(partition
);
54 if (startPartition
== position
) {
55 return values
->ValueAt(partition
);
60 template <typename ParamType
>
61 void SetValueAt(int position
, ParamType
&&value
) {
62 assert(position
< Length());
63 const int partition
= starts
->PartitionFromPosition(position
);
64 const int startPartition
= starts
->PositionFromPartition(partition
);
66 // Setting the empty value is equivalent to deleting the position
68 ClearValue(partition
);
69 } else if (position
== startPartition
) {
70 // Currently an element at this position, so remove
71 ClearValue(partition
);
72 starts
->RemovePartition(partition
);
73 values
->Delete(partition
);
75 // Else element remains empty
77 if (position
== startPartition
) {
78 // Already a value at this position, so replace
79 ClearValue(partition
);
80 values
->SetValueAt(partition
, std::move(value
));
82 // Insert a new element
83 starts
->InsertPartition(partition
+ 1, position
);
84 values
->Insert(partition
+ 1, std::move(value
));
88 void InsertSpace(int position
, int insertLength
) {
89 assert(position
<= Length()); // Only operation that works at end.
90 const int partition
= starts
->PartitionFromPosition(position
);
91 const int startPartition
= starts
->PositionFromPartition(partition
);
92 if (startPartition
== position
) {
93 const bool positionOccupied
= values
->ValueAt(partition
) != T();
94 // Inserting at start of run so make previous longer
96 // Inserting at start of document so ensure start empty
97 if (positionOccupied
) {
98 starts
->InsertPartition(1, 0);
99 values
->InsertEmpty(0, 1);
101 starts
->InsertText(partition
, insertLength
);
103 if (positionOccupied
) {
104 starts
->InsertText(partition
- 1, insertLength
);
106 // Insert at end of run so do not extend style
107 starts
->InsertText(partition
, insertLength
);
111 starts
->InsertText(partition
, insertLength
);
114 void DeletePosition(int position
) {
115 assert(position
< Length());
116 int partition
= starts
->PartitionFromPosition(position
);
117 const int startPartition
= starts
->PositionFromPartition(partition
);
118 if (startPartition
== position
) {
119 if (partition
== 0) {
121 } else if (partition
== starts
->Partitions()) {
122 // This should not be possible
123 ClearValue(partition
);
124 throw std::runtime_error("SparseVector: deleting end partition.");
126 ClearValue(partition
);
127 starts
->RemovePartition(partition
);
128 values
->Delete(partition
);
129 // Its the previous partition now that gets smaller
133 starts
->InsertText(partition
, -1);
137 throw std::runtime_error("SparseVector: Length can not be negative.");
139 if (starts
->Partitions() < 1) {
140 throw std::runtime_error("SparseVector: Must always have 1 or more partitions.");
142 if (starts
->Partitions() != values
->Length() - 1) {
143 throw std::runtime_error("SparseVector: Partitions and values different lengths.");
145 // The final element can not be set
146 if (values
->ValueAt(values
->Length() - 1) != T()) {
147 throw std::runtime_error("SparseVector: Unused style at end changed.");