tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

txNodeSet.cpp (15235B)


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