/src/mozilla-central/dom/xslt/xpath/txNodeSet.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
3 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
4 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
5 | | |
6 | | #include "txNodeSet.h" |
7 | | #include "txLog.h" |
8 | | #include "nsMemory.h" |
9 | | #include "txXPathTreeWalker.h" |
10 | | #include <algorithm> |
11 | | |
12 | | /** |
13 | | * Implementation of an XPath nodeset |
14 | | */ |
15 | | |
16 | | #ifdef NS_BUILD_REFCNT_LOGGING |
17 | | #define LOG_CHUNK_MOVE(_start, _new_start, _count) \ |
18 | | { \ |
19 | | txXPathNode *start = const_cast<txXPathNode*>(_start); \ |
20 | | while (start < _start + _count) { \ |
21 | | NS_LogDtor(start, "txXPathNode", sizeof(*start)); \ |
22 | | ++start; \ |
23 | | } \ |
24 | | start = const_cast<txXPathNode*>(_new_start); \ |
25 | | while (start < _new_start + _count) { \ |
26 | | NS_LogCtor(start, "txXPathNode", sizeof(*start)); \ |
27 | | ++start; \ |
28 | | } \ |
29 | | } |
30 | | #else |
31 | | #define LOG_CHUNK_MOVE(_start, _new_start, _count) |
32 | | #endif |
33 | | |
34 | | static const int32_t kTxNodeSetMinSize = 4; |
35 | | static const int32_t kTxNodeSetGrowFactor = 2; |
36 | | |
37 | 0 | #define kForward 1 |
38 | 0 | #define kReversed -1 |
39 | | |
40 | | txNodeSet::txNodeSet(txResultRecycler* aRecycler) |
41 | | : txAExprResult(aRecycler), |
42 | | mStart(nullptr), |
43 | | mEnd(nullptr), |
44 | | mStartBuffer(nullptr), |
45 | | mEndBuffer(nullptr), |
46 | | mDirection(kForward), |
47 | | mMarks(nullptr) |
48 | 0 | { |
49 | 0 | } |
50 | | |
51 | | txNodeSet::txNodeSet(const txXPathNode& aNode, txResultRecycler* aRecycler) |
52 | | : txAExprResult(aRecycler), |
53 | | mStart(nullptr), |
54 | | mEnd(nullptr), |
55 | | mStartBuffer(nullptr), |
56 | | mEndBuffer(nullptr), |
57 | | mDirection(kForward), |
58 | | mMarks(nullptr) |
59 | 0 | { |
60 | 0 | if (!ensureGrowSize(1)) { |
61 | 0 | return; |
62 | 0 | } |
63 | 0 | |
64 | 0 | new(mStart) txXPathNode(aNode); |
65 | 0 | ++mEnd; |
66 | 0 | } |
67 | | |
68 | | txNodeSet::txNodeSet(const txNodeSet& aSource, txResultRecycler* aRecycler) |
69 | | : txAExprResult(aRecycler), |
70 | | mStart(nullptr), |
71 | | mEnd(nullptr), |
72 | | mStartBuffer(nullptr), |
73 | | mEndBuffer(nullptr), |
74 | | mDirection(kForward), |
75 | | mMarks(nullptr) |
76 | 0 | { |
77 | 0 | append(aSource); |
78 | 0 | } |
79 | | |
80 | | txNodeSet::~txNodeSet() |
81 | 0 | { |
82 | 0 | delete [] mMarks; |
83 | 0 |
|
84 | 0 | if (mStartBuffer) { |
85 | 0 | destroyElements(mStart, mEnd); |
86 | 0 |
|
87 | 0 | free(mStartBuffer); |
88 | 0 | } |
89 | 0 | } |
90 | | |
91 | | nsresult txNodeSet::add(const txXPathNode& aNode) |
92 | 0 | { |
93 | 0 | NS_ASSERTION(mDirection == kForward, |
94 | 0 | "only append(aNode) is supported on reversed nodesets"); |
95 | 0 |
|
96 | 0 | if (isEmpty()) { |
97 | 0 | return append(aNode); |
98 | 0 | } |
99 | 0 | |
100 | 0 | bool dupe; |
101 | 0 | txXPathNode* pos = findPosition(aNode, mStart, mEnd, dupe); |
102 | 0 |
|
103 | 0 | if (dupe) { |
104 | 0 | return NS_OK; |
105 | 0 | } |
106 | 0 | |
107 | 0 | // save pos, ensureGrowSize messes with the pointers |
108 | 0 | int32_t moveSize = mEnd - pos; |
109 | 0 | int32_t offset = pos - mStart; |
110 | 0 | if (!ensureGrowSize(1)) { |
111 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
112 | 0 | } |
113 | 0 | // set pos to where it was |
114 | 0 | pos = mStart + offset; |
115 | 0 |
|
116 | 0 | if (moveSize > 0) { |
117 | 0 | LOG_CHUNK_MOVE(pos, pos + 1, moveSize); |
118 | 0 | memmove(pos + 1, pos, moveSize * sizeof(txXPathNode)); |
119 | 0 | } |
120 | 0 |
|
121 | 0 | new(pos) txXPathNode(aNode); |
122 | 0 | ++mEnd; |
123 | 0 |
|
124 | 0 | return NS_OK; |
125 | 0 | } |
126 | | |
127 | | nsresult txNodeSet::add(const txNodeSet& aNodes) |
128 | 0 | { |
129 | 0 | return add(aNodes, copyElements, nullptr); |
130 | 0 | } |
131 | | |
132 | | nsresult txNodeSet::addAndTransfer(txNodeSet* aNodes) |
133 | 0 | { |
134 | 0 | // failure is out-of-memory, transfer didn't happen |
135 | 0 | nsresult rv = add(*aNodes, transferElements, destroyElements); |
136 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
137 | 0 |
|
138 | | #ifdef TX_DONT_RECYCLE_BUFFER |
139 | | if (aNodes->mStartBuffer) { |
140 | | free(aNodes->mStartBuffer); |
141 | | aNodes->mStartBuffer = aNodes->mEndBuffer = nullptr; |
142 | | } |
143 | | #endif |
144 | 0 | aNodes->mStart = aNodes->mEnd = aNodes->mStartBuffer; |
145 | 0 |
|
146 | 0 | return NS_OK; |
147 | 0 | } |
148 | | |
149 | | /** |
150 | | * add(aNodeSet, aTransferOp) |
151 | | * |
152 | | * The code is optimized to make a minimum number of calls to |
153 | | * Node::compareDocumentPosition. The idea is this: |
154 | | * We have the two nodesets (number indicate "document position") |
155 | | * |
156 | | * 1 3 7 <- source 1 |
157 | | * 2 3 6 8 9 <- source 2 |
158 | | * _ _ _ _ _ _ _ _ <- result |
159 | | * |
160 | | * |
161 | | * When merging these nodesets into the result, the nodes are transfered |
162 | | * in chunks to the end of the buffer so that each chunk does not contain |
163 | | * a node from the other nodeset, in document order. |
164 | | * |
165 | | * We select the last non-transfered node in the first nodeset and find |
166 | | * where in the other nodeset it would be inserted. In this case we would |
167 | | * take the 7 from the first nodeset and find the position between the |
168 | | * 6 and 8 in the second. We then take the nodes after the insert-position |
169 | | * and transfer them to the end of the resulting nodeset. Which in this case |
170 | | * means that we first transfered the 8 and 9 nodes, giving us the following: |
171 | | * |
172 | | * 1 3 7 <- source 1 |
173 | | * 2 3 6 <- source 2 |
174 | | * _ _ _ _ _ _ 8 9 <- result |
175 | | * |
176 | | * The corresponding procedure is done for the second nodeset, that is |
177 | | * the insertion position of the 6 in the first nodeset is found, which |
178 | | * is between the 3 and the 7. The 7 is memmoved (as it stays within |
179 | | * the same nodeset) to the result buffer. |
180 | | * |
181 | | * As the result buffer is filled from the end, it is safe to share the |
182 | | * buffer between this nodeset and the result. |
183 | | * |
184 | | * This is repeated until both of the nodesets are empty. |
185 | | * |
186 | | * If we find a duplicate node when searching for where insertposition we |
187 | | * check for sequences of duplicate nodes, which can be optimized. |
188 | | * |
189 | | */ |
190 | | nsresult txNodeSet::add(const txNodeSet& aNodes, transferOp aTransfer, |
191 | | destroyOp aDestroy) |
192 | 0 | { |
193 | 0 | NS_ASSERTION(mDirection == kForward, |
194 | 0 | "only append(aNode) is supported on reversed nodesets"); |
195 | 0 |
|
196 | 0 | if (aNodes.isEmpty()) { |
197 | 0 | return NS_OK; |
198 | 0 | } |
199 | 0 | |
200 | 0 | if (!ensureGrowSize(aNodes.size())) { |
201 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
202 | 0 | } |
203 | 0 | |
204 | 0 | // This is probably a rather common case, so lets try to shortcut. |
205 | 0 | if (mStart == mEnd || |
206 | 0 | txXPathNodeUtils::comparePosition(mEnd[-1], *aNodes.mStart) < 0) { |
207 | 0 | aTransfer(mEnd, aNodes.mStart, aNodes.mEnd); |
208 | 0 | mEnd += aNodes.size(); |
209 | 0 |
|
210 | 0 | return NS_OK; |
211 | 0 | } |
212 | 0 | |
213 | 0 | // Last element in this nodeset |
214 | 0 | txXPathNode* thisPos = mEnd; |
215 | 0 |
|
216 | 0 | // Last element of the other nodeset |
217 | 0 | txXPathNode* otherPos = aNodes.mEnd; |
218 | 0 |
|
219 | 0 | // Pointer to the insertion point in this nodeset |
220 | 0 | txXPathNode* insertPos = mEndBuffer; |
221 | 0 |
|
222 | 0 | bool dupe; |
223 | 0 | txXPathNode* pos; |
224 | 0 | int32_t count; |
225 | 0 | while (thisPos > mStart || otherPos > aNodes.mStart) { |
226 | 0 | // Find where the last remaining node of this nodeset would |
227 | 0 | // be inserted in the other nodeset. |
228 | 0 | if (thisPos > mStart) { |
229 | 0 | pos = findPosition(thisPos[-1], aNodes.mStart, otherPos, dupe); |
230 | 0 |
|
231 | 0 | if (dupe) { |
232 | 0 | const txXPathNode *deletePos = thisPos; |
233 | 0 | --thisPos; // this is already added |
234 | 0 | // check dupe sequence |
235 | 0 | while (thisPos > mStart && pos > aNodes.mStart && |
236 | 0 | thisPos[-1] == pos[-1]) { |
237 | 0 | --thisPos; |
238 | 0 | --pos; |
239 | 0 | } |
240 | 0 |
|
241 | 0 | if (aDestroy) { |
242 | 0 | aDestroy(thisPos, deletePos); |
243 | 0 | } |
244 | 0 | } |
245 | 0 | } |
246 | 0 | else { |
247 | 0 | pos = aNodes.mStart; |
248 | 0 | } |
249 | 0 |
|
250 | 0 | // Transfer the otherNodes after the insertion point to the result |
251 | 0 | count = otherPos - pos; |
252 | 0 | if (count > 0) { |
253 | 0 | insertPos -= count; |
254 | 0 | aTransfer(insertPos, pos, otherPos); |
255 | 0 | otherPos -= count; |
256 | 0 | } |
257 | 0 |
|
258 | 0 | // Find where the last remaining node of the otherNodeset would |
259 | 0 | // be inserted in this nodeset. |
260 | 0 | if (otherPos > aNodes.mStart) { |
261 | 0 | pos = findPosition(otherPos[-1], mStart, thisPos, dupe); |
262 | 0 |
|
263 | 0 | if (dupe) { |
264 | 0 | const txXPathNode *deletePos = otherPos; |
265 | 0 | --otherPos; // this is already added |
266 | 0 | // check dupe sequence |
267 | 0 | while (otherPos > aNodes.mStart && pos > mStart && |
268 | 0 | otherPos[-1] == pos[-1]) { |
269 | 0 | --otherPos; |
270 | 0 | --pos; |
271 | 0 | } |
272 | 0 |
|
273 | 0 | if (aDestroy) { |
274 | 0 | aDestroy(otherPos, deletePos); |
275 | 0 | } |
276 | 0 | } |
277 | 0 | } |
278 | 0 | else { |
279 | 0 | pos = mStart; |
280 | 0 | } |
281 | 0 |
|
282 | 0 | // Move the nodes from this nodeset after the insertion point |
283 | 0 | // to the result |
284 | 0 | count = thisPos - pos; |
285 | 0 | if (count > 0) { |
286 | 0 | insertPos -= count; |
287 | 0 | LOG_CHUNK_MOVE(pos, insertPos, count); |
288 | 0 | memmove(insertPos, pos, count * sizeof(txXPathNode)); |
289 | 0 | thisPos -= count; |
290 | 0 | } |
291 | 0 | } |
292 | 0 | mStart = insertPos; |
293 | 0 | mEnd = mEndBuffer; |
294 | 0 |
|
295 | 0 | return NS_OK; |
296 | 0 | } |
297 | | |
298 | | /** |
299 | | * Append API |
300 | | * These functions should be used with care. |
301 | | * They are intended to be used when the caller assures that the resulting |
302 | | * nodeset remains in document order. |
303 | | * Abuse will break document order, and cause errors in the result. |
304 | | * These functions are significantly faster than the add API, as no |
305 | | * order info operations will be performed. |
306 | | */ |
307 | | |
308 | | nsresult |
309 | | txNodeSet::append(const txXPathNode& aNode) |
310 | 0 | { |
311 | 0 | if (!ensureGrowSize(1)) { |
312 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
313 | 0 | } |
314 | 0 | |
315 | 0 | if (mDirection == kForward) { |
316 | 0 | new(mEnd) txXPathNode(aNode); |
317 | 0 | ++mEnd; |
318 | 0 |
|
319 | 0 | return NS_OK; |
320 | 0 | } |
321 | 0 | |
322 | 0 | new(--mStart) txXPathNode(aNode); |
323 | 0 |
|
324 | 0 | return NS_OK; |
325 | 0 | } |
326 | | |
327 | | nsresult |
328 | | txNodeSet::append(const txNodeSet& aNodes) |
329 | 0 | { |
330 | 0 | NS_ASSERTION(mDirection == kForward, |
331 | 0 | "only append(aNode) is supported on reversed nodesets"); |
332 | 0 |
|
333 | 0 | if (aNodes.isEmpty()) { |
334 | 0 | return NS_OK; |
335 | 0 | } |
336 | 0 | |
337 | 0 | int32_t appended = aNodes.size(); |
338 | 0 | if (!ensureGrowSize(appended)) { |
339 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
340 | 0 | } |
341 | 0 | |
342 | 0 | copyElements(mEnd, aNodes.mStart, aNodes.mEnd); |
343 | 0 | mEnd += appended; |
344 | 0 |
|
345 | 0 | return NS_OK; |
346 | 0 | } |
347 | | |
348 | | nsresult |
349 | | txNodeSet::mark(int32_t aIndex) |
350 | 0 | { |
351 | 0 | NS_ASSERTION(aIndex >= 0 && mStart && mEnd - mStart > aIndex, |
352 | 0 | "index out of bounds"); |
353 | 0 | if (!mMarks) { |
354 | 0 | int32_t length = size(); |
355 | 0 | mMarks = new bool[length]; |
356 | 0 | memset(mMarks, 0, length * sizeof(bool)); |
357 | 0 | } |
358 | 0 | if (mDirection == kForward) { |
359 | 0 | mMarks[aIndex] = true; |
360 | 0 | } |
361 | 0 | else { |
362 | 0 | mMarks[size() - aIndex - 1] = true; |
363 | 0 | } |
364 | 0 |
|
365 | 0 | return NS_OK; |
366 | 0 | } |
367 | | |
368 | | nsresult |
369 | | txNodeSet::sweep() |
370 | 0 | { |
371 | 0 | if (!mMarks) { |
372 | 0 | // sweep everything |
373 | 0 | clear(); |
374 | 0 | } |
375 | 0 |
|
376 | 0 | int32_t chunk, pos = 0; |
377 | 0 | int32_t length = size(); |
378 | 0 | txXPathNode* insertion = mStartBuffer; |
379 | 0 |
|
380 | 0 | while (pos < length) { |
381 | 0 | while (pos < length && !mMarks[pos]) { |
382 | 0 | // delete unmarked |
383 | 0 | mStart[pos].~txXPathNode(); |
384 | 0 | ++pos; |
385 | 0 | } |
386 | 0 | // find chunk to move |
387 | 0 | chunk = 0; |
388 | 0 | while (pos < length && mMarks[pos]) { |
389 | 0 | ++pos; |
390 | 0 | ++chunk; |
391 | 0 | } |
392 | 0 | // move chunk |
393 | 0 | if (chunk > 0) { |
394 | 0 | LOG_CHUNK_MOVE(mStart + pos - chunk, insertion, chunk); |
395 | 0 | memmove(insertion, mStart + pos - chunk, |
396 | 0 | chunk * sizeof(txXPathNode)); |
397 | 0 | insertion += chunk; |
398 | 0 | } |
399 | 0 | } |
400 | 0 | mStart = mStartBuffer; |
401 | 0 | mEnd = insertion; |
402 | 0 | delete [] mMarks; |
403 | 0 | mMarks = nullptr; |
404 | 0 |
|
405 | 0 | return NS_OK; |
406 | 0 | } |
407 | | |
408 | | void |
409 | | txNodeSet::clear() |
410 | 0 | { |
411 | 0 | destroyElements(mStart, mEnd); |
412 | | #ifdef TX_DONT_RECYCLE_BUFFER |
413 | | if (mStartBuffer) { |
414 | | free(mStartBuffer); |
415 | | mStartBuffer = mEndBuffer = nullptr; |
416 | | } |
417 | | #endif |
418 | | mStart = mEnd = mStartBuffer; |
419 | 0 | delete [] mMarks; |
420 | 0 | mMarks = nullptr; |
421 | 0 | mDirection = kForward; |
422 | 0 | } |
423 | | |
424 | | int32_t |
425 | | txNodeSet::indexOf(const txXPathNode& aNode, uint32_t aStart) const |
426 | 0 | { |
427 | 0 | NS_ASSERTION(mDirection == kForward, |
428 | 0 | "only append(aNode) is supported on reversed nodesets"); |
429 | 0 |
|
430 | 0 | if (!mStart || mStart == mEnd) { |
431 | 0 | return -1; |
432 | 0 | } |
433 | 0 | |
434 | 0 | txXPathNode* pos = mStart + aStart; |
435 | 0 | for (; pos < mEnd; ++pos) { |
436 | 0 | if (*pos == aNode) { |
437 | 0 | return pos - mStart; |
438 | 0 | } |
439 | 0 | } |
440 | 0 |
|
441 | 0 | return -1; |
442 | 0 | } |
443 | | |
444 | | const txXPathNode& |
445 | | txNodeSet::get(int32_t aIndex) const |
446 | 0 | { |
447 | 0 | if (mDirection == kForward) { |
448 | 0 | return mStart[aIndex]; |
449 | 0 | } |
450 | 0 | |
451 | 0 | return mEnd[-aIndex - 1]; |
452 | 0 | } |
453 | | |
454 | | short |
455 | | txNodeSet::getResultType() |
456 | 0 | { |
457 | 0 | return txAExprResult::NODESET; |
458 | 0 | } |
459 | | |
460 | | bool |
461 | | txNodeSet::booleanValue() |
462 | 0 | { |
463 | 0 | return !isEmpty(); |
464 | 0 | } |
465 | | double |
466 | | txNodeSet::numberValue() |
467 | 0 | { |
468 | 0 | nsAutoString str; |
469 | 0 | stringValue(str); |
470 | 0 |
|
471 | 0 | return txDouble::toDouble(str); |
472 | 0 | } |
473 | | |
474 | | void |
475 | | txNodeSet::stringValue(nsString& aStr) |
476 | 0 | { |
477 | 0 | NS_ASSERTION(mDirection == kForward, |
478 | 0 | "only append(aNode) is supported on reversed nodesets"); |
479 | 0 | if (isEmpty()) { |
480 | 0 | return; |
481 | 0 | } |
482 | 0 | txXPathNodeUtils::appendNodeValue(get(0), aStr); |
483 | 0 | } |
484 | | |
485 | | const nsString* |
486 | | txNodeSet::stringValuePointer() |
487 | 0 | { |
488 | 0 | return nullptr; |
489 | 0 | } |
490 | | |
491 | | bool txNodeSet::ensureGrowSize(int32_t aSize) |
492 | 0 | { |
493 | 0 | // check if there is enough place in the buffer as is |
494 | 0 | if (mDirection == kForward && aSize <= mEndBuffer - mEnd) { |
495 | 0 | return true; |
496 | 0 | } |
497 | 0 | |
498 | 0 | if (mDirection == kReversed && aSize <= mStart - mStartBuffer) { |
499 | 0 | return true; |
500 | 0 | } |
501 | 0 | |
502 | 0 | // check if we just have to align mStart to have enough space |
503 | 0 | int32_t oldSize = mEnd - mStart; |
504 | 0 | int32_t oldLength = mEndBuffer - mStartBuffer; |
505 | 0 | int32_t ensureSize = oldSize + aSize; |
506 | 0 | if (ensureSize <= oldLength) { |
507 | 0 | // just move the buffer |
508 | 0 | txXPathNode* dest = mStartBuffer; |
509 | 0 | if (mDirection == kReversed) { |
510 | 0 | dest = mEndBuffer - oldSize; |
511 | 0 | } |
512 | 0 | LOG_CHUNK_MOVE(mStart, dest, oldSize); |
513 | 0 | memmove(dest, mStart, oldSize * sizeof(txXPathNode)); |
514 | 0 | mStart = dest; |
515 | 0 | mEnd = dest + oldSize; |
516 | 0 |
|
517 | 0 | return true; |
518 | 0 | } |
519 | 0 |
|
520 | 0 | // This isn't 100% safe. But until someone manages to make a 1gig nodeset |
521 | 0 | // it should be ok. |
522 | 0 | int32_t newLength = std::max(oldLength, kTxNodeSetMinSize); |
523 | 0 |
|
524 | 0 | while (newLength < ensureSize) { |
525 | 0 | newLength *= kTxNodeSetGrowFactor; |
526 | 0 | } |
527 | 0 |
|
528 | 0 | txXPathNode* newArr = |
529 | 0 | static_cast<txXPathNode*>(moz_xmalloc(newLength * sizeof(txXPathNode))); |
530 | 0 |
|
531 | 0 | txXPathNode* dest = newArr; |
532 | 0 | if (mDirection == kReversed) { |
533 | 0 | dest += newLength - oldSize; |
534 | 0 | } |
535 | 0 |
|
536 | 0 | if (oldSize > 0) { |
537 | 0 | LOG_CHUNK_MOVE(mStart, dest, oldSize); |
538 | 0 | memcpy(dest, mStart, oldSize * sizeof(txXPathNode)); |
539 | 0 | } |
540 | 0 |
|
541 | 0 | if (mStartBuffer) { |
542 | | #ifdef DEBUG |
543 | | memset(mStartBuffer, 0, |
544 | | (mEndBuffer - mStartBuffer) * sizeof(txXPathNode)); |
545 | | #endif |
546 | | free(mStartBuffer); |
547 | 0 | } |
548 | 0 |
|
549 | 0 | mStartBuffer = newArr; |
550 | 0 | mEndBuffer = mStartBuffer + newLength; |
551 | 0 | mStart = dest; |
552 | 0 | mEnd = dest + oldSize; |
553 | 0 |
|
554 | 0 | return true; |
555 | 0 | } |
556 | | |
557 | | txXPathNode* |
558 | | txNodeSet::findPosition(const txXPathNode& aNode, txXPathNode* aFirst, |
559 | | txXPathNode* aLast, bool& aDupe) const |
560 | 0 | { |
561 | 0 | aDupe = false; |
562 | 0 | if (aLast - aFirst <= 2) { |
563 | 0 | // If we search 2 nodes or less there is no point in further divides |
564 | 0 | txXPathNode* pos = aFirst; |
565 | 0 | for (; pos < aLast; ++pos) { |
566 | 0 | int cmp = txXPathNodeUtils::comparePosition(aNode, *pos); |
567 | 0 | if (cmp < 0) { |
568 | 0 | return pos; |
569 | 0 | } |
570 | 0 | |
571 | 0 | if (cmp == 0) { |
572 | 0 | aDupe = true; |
573 | 0 |
|
574 | 0 | return pos; |
575 | 0 | } |
576 | 0 | } |
577 | 0 | return pos; |
578 | 0 | } |
579 | 0 | |
580 | 0 | // (cannot add two pointers) |
581 | 0 | txXPathNode* midpos = aFirst + (aLast - aFirst) / 2; |
582 | 0 | int cmp = txXPathNodeUtils::comparePosition(aNode, *midpos); |
583 | 0 | if (cmp == 0) { |
584 | 0 | aDupe = true; |
585 | 0 |
|
586 | 0 | return midpos; |
587 | 0 | } |
588 | 0 | |
589 | 0 | if (cmp > 0) { |
590 | 0 | return findPosition(aNode, midpos + 1, aLast, aDupe); |
591 | 0 | } |
592 | 0 | |
593 | 0 | // midpos excluded as end of range |
594 | 0 | |
595 | 0 | return findPosition(aNode, aFirst, midpos, aDupe); |
596 | 0 | } |
597 | | |
598 | | /* static */ |
599 | | void |
600 | | txNodeSet::copyElements(txXPathNode* aDest, |
601 | | const txXPathNode* aStart, const txXPathNode* aEnd) |
602 | 0 | { |
603 | 0 | const txXPathNode* pos = aStart; |
604 | 0 | while (pos < aEnd) { |
605 | 0 | new(aDest) txXPathNode(*pos); |
606 | 0 | ++aDest; |
607 | 0 | ++pos; |
608 | 0 | } |
609 | 0 | } |
610 | | |
611 | | /* static */ |
612 | | void |
613 | | txNodeSet::transferElements(txXPathNode* aDest, |
614 | | const txXPathNode* aStart, const txXPathNode* aEnd) |
615 | 0 | { |
616 | 0 | LOG_CHUNK_MOVE(aStart, aDest, (aEnd - aStart)); |
617 | 0 | memcpy(aDest, aStart, (aEnd - aStart) * sizeof(txXPathNode)); |
618 | 0 | } |