mirror of https://github.com/roytam1/UXP
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
135 lines
3.4 KiB
135 lines
3.4 KiB
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
/* This Source Code Form is subject to the terms of the Mozilla Public |
|
* License, v. 2.0. If a copy of the MPL was not distributed with this |
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
|
|
/** |
|
* A class to generate unique IDs in the range [ - 2^31, 0 ) |
|
*/ |
|
|
|
#ifndef MOZILLA_A11Y_IDSet_h_ |
|
#define MOZILLA_A11Y_IDSet_h_ |
|
|
|
#include "mozilla/Attributes.h" |
|
#include "mozilla/MathAlgorithms.h" |
|
#include "mozilla/SplayTree.h" |
|
|
|
namespace mozilla { |
|
namespace a11y { |
|
|
|
/** |
|
* On windows an accessible's id must be a negative 32 bit integer. It is |
|
* important to support recycling arbitrary IDs because accessibles can be |
|
* created and destroyed at any time in the life of a page. IDSet provides 2 |
|
* operations: generate an ID in the range (0, mMaxId], and release an ID so |
|
* it can be allocated again. Allocated ID are tracked by a sparse bitmap |
|
* implemented with a splay tree. Nodes in the tree are keyed by the upper N |
|
* bits of the ID, and the node contains a bitmap tracking the allocation of |
|
* 2^(ceil(log2(mMaxId)) - N) IDs. |
|
* |
|
* Note that negation is handled by MsaaIdGenerator as it performs additional |
|
* decoration on the ID generated by IDSet. |
|
* @see mozilla::a11y::MsaaIdGenerator |
|
*/ |
|
class IDSet |
|
{ |
|
public: |
|
constexpr explicit IDSet(const uint32_t aMaxIdBits) |
|
: mBitSet() |
|
, mIdx(0) |
|
, mMaxId((1UL << aMaxIdBits) - 1UL) |
|
, mMaxIdx(mMaxId / bitsPerElt) |
|
{ |
|
} |
|
|
|
/** |
|
* Return a new unique id. |
|
*/ |
|
uint32_t GetID() |
|
{ |
|
uint32_t idx = mIdx; |
|
while (true) { |
|
BitSetElt* elt = mBitSet.findOrInsert(BitSetElt(idx)); |
|
if (elt->mBitvec[0] != UINT64_MAX) { |
|
uint32_t i = CountTrailingZeroes64(~elt->mBitvec[0]); |
|
|
|
elt->mBitvec[0] |= (1ull << i); |
|
mIdx = idx; |
|
return (elt->mIdx * bitsPerElt + i); |
|
} |
|
|
|
if (elt->mBitvec[1] != UINT64_MAX) { |
|
uint32_t i = CountTrailingZeroes64(~elt->mBitvec[1]); |
|
|
|
elt->mBitvec[1] |= (1ull << i); |
|
mIdx = idx; |
|
return (elt->mIdx * bitsPerElt + bitsPerWord + i); |
|
} |
|
|
|
idx++; |
|
if (idx > mMaxIdx) { |
|
idx = 0; |
|
} |
|
|
|
if (idx == mIdx) { |
|
MOZ_CRASH("used up all the available ids"); |
|
} |
|
} |
|
} |
|
|
|
/** |
|
* Free a no longer required id so it may be allocated again. |
|
*/ |
|
void ReleaseID(uint32_t aID) |
|
{ |
|
MOZ_ASSERT(aID < mMaxId); |
|
|
|
uint32_t idx = aID / bitsPerElt; |
|
mIdx = idx; |
|
BitSetElt* elt = mBitSet.find(BitSetElt(idx)); |
|
MOZ_ASSERT(elt); |
|
|
|
uint32_t vecIdx = (aID % bitsPerElt) / bitsPerWord; |
|
elt->mBitvec[vecIdx] &= ~(1ull << (aID % bitsPerWord)); |
|
if (elt->mBitvec[0] == 0 && elt->mBitvec[1] == 0) { |
|
delete mBitSet.remove(*elt); |
|
} |
|
} |
|
|
|
private: |
|
static const unsigned int wordsPerElt = 2; |
|
static const unsigned int bitsPerWord = 64; |
|
static const unsigned int bitsPerElt = wordsPerElt * bitsPerWord; |
|
|
|
struct BitSetElt : mozilla::SplayTreeNode<BitSetElt> |
|
{ |
|
explicit BitSetElt(uint32_t aIdx) : |
|
mIdx(aIdx) |
|
{ mBitvec[0] = mBitvec[1] = 0; } |
|
|
|
uint64_t mBitvec[wordsPerElt]; |
|
uint32_t mIdx; |
|
|
|
static int compare(const BitSetElt& a, const BitSetElt& b) |
|
{ |
|
if (a.mIdx == b.mIdx) { |
|
return 0; |
|
} |
|
|
|
if (a.mIdx < b.mIdx) { |
|
return -1; |
|
} |
|
return 1; |
|
} |
|
}; |
|
|
|
SplayTree<BitSetElt, BitSetElt> mBitSet; |
|
uint32_t mIdx; |
|
const uint32_t mMaxId; |
|
const uint32_t mMaxIdx; |
|
}; |
|
|
|
} |
|
} |
|
|
|
#endif
|
|
|