1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20 #include <compressedarray.hxx>
21 #include <global.hxx>
22
23 template< typename A, typename D >
ScCompressedArray(A nMaxAccessP,const D & rValue)24 ScCompressedArray<A,D>::ScCompressedArray( A nMaxAccessP, const D& rValue )
25 : nCount(1)
26 , nLimit(1)
27 , pData( new DataEntry[1])
28 , nMaxAccess( nMaxAccessP)
29 {
30 pData[0].aValue = rValue;
31 pData[0].nEnd = nMaxAccess;
32 }
33
34 template< typename A, typename D >
~ScCompressedArray()35 ScCompressedArray<A,D>::~ScCompressedArray()
36 {
37 }
38
39 template< typename A, typename D >
Search(A nAccess) const40 size_t ScCompressedArray<A,D>::Search( A nAccess ) const
41 {
42 if (nAccess == 0)
43 return 0;
44
45 tools::Long nLo = 0;
46 tools::Long nHi = static_cast<tools::Long>(nCount) - 1;
47 tools::Long nStart = 0;
48 tools::Long i = 0;
49 bool bFound = (nCount == 1);
50 while (!bFound && nLo <= nHi)
51 {
52 i = (nLo + nHi) / 2;
53 if (i > 0)
54 nStart = static_cast<tools::Long>(pData[i - 1].nEnd);
55 else
56 nStart = -1;
57 tools::Long nEnd = static_cast<tools::Long>(pData[i].nEnd);
58 if (nEnd < static_cast<tools::Long>(nAccess))
59 nLo = ++i;
60 else
61 if (nStart >= static_cast<tools::Long>(nAccess))
62 nHi = --i;
63 else
64 bFound = true;
65 }
66 return (bFound ? static_cast<size_t>(i) : (nAccess < 0 ? 0 : nCount-1));
67 }
68
69 template< typename A, typename D >
SetValue(A nStart,A nEnd,const D & rValue)70 void ScCompressedArray<A,D>::SetValue( A nStart, A nEnd, const D& rValue )
71 {
72 if (!(0 <= nStart && nStart <= nMaxAccess && 0 <= nEnd && nEnd <= nMaxAccess
73 && nStart <= nEnd))
74 return;
75
76 if ((nStart == 0) && (nEnd == nMaxAccess))
77 Reset( rValue);
78 else
79 {
80 // Create a temporary copy in case we got a reference passed that
81 // points to a part of the array to be reallocated.
82 D aNewVal( rValue);
83 size_t nNeeded = nCount + 2;
84 if (nLimit < nNeeded)
85 {
86 nLimit *= 1.5;
87 if (nLimit < nNeeded)
88 nLimit = nNeeded;
89 std::unique_ptr<DataEntry[]> pNewData(new DataEntry[nLimit]);
90 memcpy( pNewData.get(), pData.get(), nCount*sizeof(DataEntry));
91 pData = std::move(pNewData);
92 }
93
94 size_t ni; // number of leading entries
95 size_t nInsert; // insert position (nMaxAccess+1 := no insert)
96 bool bCombined = false;
97 bool bSplit = false;
98 if (nStart > 0)
99 {
100 // skip leading
101 ni = this->Search( nStart);
102
103 nInsert = nMaxAccess+1;
104 if (!(pData[ni].aValue == aNewVal))
105 {
106 if (ni == 0 || (pData[ni-1].nEnd < nStart - 1))
107 { // may be a split or a simple insert or just a shrink,
108 // row adjustment is done further down
109 if (pData[ni].nEnd > nEnd)
110 bSplit = true;
111 ni++;
112 nInsert = ni;
113 }
114 else if (ni > 0 && pData[ni-1].nEnd == nStart - 1)
115 nInsert = ni;
116 }
117 if (ni > 0 && pData[ni-1].aValue == aNewVal)
118 { // combine
119 pData[ni-1].nEnd = nEnd;
120 nInsert = nMaxAccess+1;
121 bCombined = true;
122 }
123 }
124 else
125 {
126 nInsert = 0;
127 ni = 0;
128 }
129
130 size_t nj = ni; // stop position of range to replace
131 while (nj < nCount && pData[nj].nEnd <= nEnd)
132 nj++;
133 if (!bSplit)
134 {
135 if (nj < nCount && pData[nj].aValue == aNewVal)
136 { // combine
137 if (ni > 0)
138 {
139 if (pData[ni-1].aValue == aNewVal)
140 { // adjacent entries
141 pData[ni-1].nEnd = pData[nj].nEnd;
142 nj++;
143 }
144 else if (ni == nInsert)
145 pData[ni-1].nEnd = nStart - 1; // shrink
146 }
147 nInsert = nMaxAccess+1;
148 bCombined = true;
149 }
150 else if (ni > 0 && ni == nInsert)
151 pData[ni-1].nEnd = nStart - 1; // shrink
152 }
153 if (ni < nj)
154 { // remove middle entries
155 if (!bCombined)
156 { // replace one entry
157 pData[ni].nEnd = nEnd;
158 pData[ni].aValue = aNewVal;
159 ni++;
160 nInsert = nMaxAccess+1;
161 }
162 if (ni < nj)
163 { // remove entries
164 memmove( pData.get() + ni, pData.get() + nj,
165 (nCount - nj) * sizeof(DataEntry));
166 nCount -= nj - ni;
167 }
168 }
169
170 if (nInsert < static_cast<size_t>(nMaxAccess+1))
171 { // insert or append new entry
172 if (nInsert <= nCount)
173 {
174 if (!bSplit)
175 memmove( pData.get() + nInsert + 1, pData.get() + nInsert,
176 (nCount - nInsert) * sizeof(DataEntry));
177 else
178 {
179 memmove( pData.get() + nInsert + 2, pData.get() + nInsert,
180 (nCount - nInsert) * sizeof(DataEntry));
181 pData[nInsert+1] = pData[nInsert-1];
182 nCount++;
183 }
184 }
185 if (nInsert)
186 pData[nInsert-1].nEnd = nStart - 1;
187 pData[nInsert].nEnd = nEnd;
188 pData[nInsert].aValue = aNewVal;
189 nCount++;
190 }
191 }
192 }
193
194 template< typename A, typename D >
CopyFrom(const ScCompressedArray<A,D> & rArray,A nDestStart,A nDestEnd,A nSrcStart)195 void ScCompressedArray<A,D>::CopyFrom( const ScCompressedArray<A,D>& rArray, A nDestStart,
196 A nDestEnd, A nSrcStart )
197 {
198 assert( this != &rArray && "cannot copy self->self" );
199 size_t nIndex = 0;
200 A nRegionEnd;
201 for (A j=nDestStart; j<=nDestEnd; ++j)
202 {
203 const D& rValue = (j==nDestStart ?
204 rArray.GetValue( j - nDestStart + nSrcStart, nIndex, nRegionEnd) :
205 rArray.GetNextValue( nIndex, nRegionEnd));
206 nRegionEnd = nRegionEnd - nSrcStart + nDestStart;
207 if (nRegionEnd > nDestEnd)
208 nRegionEnd = nDestEnd;
209 this->SetValue( j, nRegionEnd, rValue);
210 j = nRegionEnd;
211 }
212 }
213
214 template< typename A, typename D >
Insert(A nStart,size_t nAccessCount)215 const D& ScCompressedArray<A,D>::Insert( A nStart, size_t nAccessCount )
216 {
217 size_t nIndex = this->Search( nStart);
218 // No real insertion is needed, simply extend the one entry and adapt all
219 // following. In case nStart points to the start row of an entry, extend
220 // the previous entry (inserting before nStart).
221 if (nIndex > 0 && pData[nIndex-1].nEnd+1 == nStart)
222 --nIndex;
223 const D& rValue = pData[nIndex].aValue; // the value "copied"
224 do
225 {
226 pData[nIndex].nEnd += nAccessCount;
227 if (pData[nIndex].nEnd >= nMaxAccess)
228 {
229 pData[nIndex].nEnd = nMaxAccess;
230 nCount = nIndex + 1; // discard trailing entries
231 }
232 } while (++nIndex < nCount);
233 return rValue;
234 }
235
236 template< typename A, typename D >
InsertPreservingSize(A nStart,size_t nAccessCount,const D & rFillValue)237 void ScCompressedArray<A,D>::InsertPreservingSize( A nStart, size_t nAccessCount, const D& rFillValue )
238 {
239 const A nPrevLastPos = GetLastPos();
240
241 Insert(nStart, nAccessCount);
242 for (A i = nStart; i < A(nStart + nAccessCount); ++i)
243 SetValue(i, rFillValue);
244
245 const A nNewLastPos = GetLastPos();
246 Remove(nPrevLastPos, nNewLastPos - nPrevLastPos);
247 }
248
249 template< typename A, typename D >
Remove(A nStart,size_t nAccessCount)250 void ScCompressedArray<A,D>::Remove( A nStart, size_t nAccessCount )
251 {
252 A nEnd = nStart + nAccessCount - 1;
253 size_t nIndex = this->Search( nStart);
254 // equalize/combine/remove all entries in between
255 if (nEnd > pData[nIndex].nEnd)
256 this->SetValue( nStart, nEnd, pData[nIndex].aValue);
257 // remove an exactly matching entry by shifting up all following by one
258 if ((nStart == 0 || (nIndex > 0 && nStart == pData[nIndex-1].nEnd+1)) &&
259 pData[nIndex].nEnd == nEnd && nIndex < nCount-1)
260 {
261 // In case removing an entry results in two adjacent entries with
262 // identical data, combine them into one. This is also necessary to
263 // make the algorithm used in SetValue() work correctly, it relies on
264 // the fact that consecutive values actually differ.
265 size_t nRemove;
266 if (nIndex > 0 && pData[nIndex-1].aValue == pData[nIndex+1].aValue)
267 {
268 nRemove = 2;
269 --nIndex;
270 }
271 else
272 nRemove = 1;
273 memmove( pData.get() + nIndex, pData.get() + nIndex + nRemove, (nCount - (nIndex +
274 nRemove)) * sizeof(DataEntry));
275 nCount -= nRemove;
276 }
277 // adjust end rows, nIndex still being valid
278 do
279 {
280 pData[nIndex].nEnd -= nAccessCount;
281 } while (++nIndex < nCount);
282 pData[nCount-1].nEnd = nMaxAccess;
283 }
284
285 template< typename A, typename D >
RemovePreservingSize(A nStart,size_t nAccessCount,const D & rFillValue)286 void ScCompressedArray<A,D>::RemovePreservingSize( A nStart, size_t nAccessCount, const D& rFillValue )
287 {
288 const A nPrevLastPos = GetLastPos();
289
290 Remove(nStart, nAccessCount);
291
292 const A nNewLastPos = GetLastPos();
293 InsertPreservingSize(nNewLastPos, nNewLastPos - nPrevLastPos, rFillValue);
294 }
295
296 template< typename A, typename D >
operator ++()297 void ScCompressedArray<A,D>::Iterator::operator++()
298 {
299 ++mnRegion;
300 if (mnRegion > mrArray.pData[mnIndex].nEnd)
301 ++mnIndex;
302 }
303
304 template< typename A, typename D >
operator +(size_t nAccessCount) const305 typename ScCompressedArray<A,D>::Iterator ScCompressedArray<A,D>::Iterator::operator+(size_t nAccessCount) const
306 {
307 A nRegion = mnRegion + nAccessCount;
308 auto nIndex = mnIndex;
309 while (nRegion > mrArray.pData[nIndex].nEnd)
310 ++nIndex;
311 return Iterator(mrArray, nIndex, nRegion);
312 }
313
314 // === ScBitMaskCompressedArray ==============================================
315
316 template< typename A, typename D >
AndValue(A nStart,A nEnd,const D & rValueToAnd)317 void ScBitMaskCompressedArray<A,D>::AndValue( A nStart, A nEnd,
318 const D& rValueToAnd )
319 {
320 if (nStart > nEnd)
321 return;
322
323 size_t nIndex = this->Search( nStart);
324 do
325 {
326 if ((this->pData[nIndex].aValue & rValueToAnd) != this->pData[nIndex].aValue)
327 {
328 A nS = ::std::max<A>( (nIndex>0 ? this->pData[nIndex-1].nEnd+1 : 0), nStart);
329 A nE = ::std::min( this->pData[nIndex].nEnd, nEnd);
330 this->SetValue( nS, nE, this->pData[nIndex].aValue & rValueToAnd);
331 if (nE >= nEnd)
332 break; // while
333 nIndex = this->Search( nE + 1);
334 }
335 else if (this->pData[nIndex].nEnd >= nEnd)
336 break; // while
337 else
338 ++nIndex;
339 } while (nIndex < this->nCount);
340 }
341
342 template< typename A, typename D >
OrValue(A nStart,A nEnd,const D & rValueToOr)343 void ScBitMaskCompressedArray<A,D>::OrValue( A nStart, A nEnd,
344 const D& rValueToOr )
345 {
346 if (nStart > nEnd)
347 return;
348
349 size_t nIndex = this->Search( nStart);
350 do
351 {
352 if ((this->pData[nIndex].aValue | rValueToOr) != this->pData[nIndex].aValue)
353 {
354 A nS = ::std::max<A>( (nIndex>0 ? this->pData[nIndex-1].nEnd+1 : 0), nStart);
355 A nE = ::std::min( this->pData[nIndex].nEnd, nEnd);
356 this->SetValue( nS, nE, this->pData[nIndex].aValue | rValueToOr);
357 if (nE >= nEnd)
358 break; // while
359 nIndex = this->Search( nE + 1);
360 }
361 else if (this->pData[nIndex].nEnd >= nEnd)
362 break; // while
363 else
364 ++nIndex;
365 } while (nIndex < this->nCount);
366 }
367
368 template< typename A, typename D >
CopyFromAnded(const ScBitMaskCompressedArray<A,D> & rArray,A nStart,A nEnd,const D & rValueToAnd)369 void ScBitMaskCompressedArray<A,D>::CopyFromAnded(
370 const ScBitMaskCompressedArray<A,D>& rArray, A nStart, A nEnd,
371 const D& rValueToAnd )
372 {
373 size_t nIndex = 0;
374 A nRegionEnd;
375 for (A j=nStart; j<=nEnd; ++j)
376 {
377 const D& rValue = (j==nStart ?
378 rArray.GetValue( j, nIndex, nRegionEnd) :
379 rArray.GetNextValue( nIndex, nRegionEnd));
380 if (nRegionEnd > nEnd)
381 nRegionEnd = nEnd;
382 this->SetValue( j, nRegionEnd, rValue & rValueToAnd);
383 j = nRegionEnd;
384 }
385 }
386
387 template< typename A, typename D >
388 A ScBitMaskCompressedArray<A,D>::GetLastAnyBitAccess( const D& rBitMask ) const
389 {
390 A nEnd = ::std::numeric_limits<A>::max();
391 size_t nIndex = this->nCount-1;
392 while (true)
393 {
394 if (this->pData[nIndex].aValue & rBitMask)
395 {
396 nEnd = this->pData[nIndex].nEnd;
397 break; // while
398 }
399 else
400 {
401 if (nIndex > 0)
402 {
403 --nIndex;
404 if (this->pData[nIndex].nEnd < 0)
405 break; // while
406 }
407 else
408 break; // while
409 }
410 }
411 return nEnd;
412 }
413
414 // === Force instantiation of specializations ================================
415
416 template class ScCompressedArray< SCROW, CRFlags>; // flags, base class
417 template class ScBitMaskCompressedArray< SCROW, CRFlags>; // flags
418 template class ScCompressedArray< SCCOL, sal_uInt16>;
419 template class ScCompressedArray< SCCOL, CRFlags>;
420 template class ScCompressedArray< SCROW, sal_uInt16>;
421 template class ScBitMaskCompressedArray< SCCOL, CRFlags>;
422
423 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
424