| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /* |
| * $Id$ |
| */ |
| |
| #if !defined(XERCESC_INCLUDE_GUARD_CMSTATESET_HPP) |
| #define XERCESC_INCLUDE_GUARD_CMSTATESET_HPP |
| |
| // DESCRIPTION: |
| // |
| // This class is a specialized bitset class for the content model code of |
| // the validator. It assumes that its never called with two objects of |
| // different bit counts, and that bit sets smaller than a threshold are far |
| // and away the most common. So it can be a lot more optimized than a general |
| // purpose utility bitset class |
| // |
| |
| #include <xercesc/util/ArrayIndexOutOfBoundsException.hpp> |
| #include <xercesc/util/RuntimeException.hpp> |
| #include <xercesc/util/PlatformUtils.hpp> |
| #include <xercesc/framework/MemoryManager.hpp> |
| #include <string.h> |
| |
| #if XERCES_HAVE_EMMINTRIN_H |
| # include <emmintrin.h> |
| #endif |
| |
| XERCES_CPP_NAMESPACE_BEGIN |
| |
| class CMStateSetEnumerator; |
| |
| // This value must be 4 in order to use the SSE2 instruction set |
| #define CMSTATE_CACHED_INT32_SIZE 4 |
| |
| // This value must be a multiple of 128 in order to use the SSE2 instruction set |
| #define CMSTATE_BITFIELD_CHUNK 1024 |
| #define CMSTATE_BITFIELD_INT32_SIZE (1024 / 32) |
| |
| struct CMDynamicBuffer |
| { |
| // fArraySize |
| // This indicates the number of elements of the fBitArray vector |
| // |
| // fBitArray |
| // A vector of arrays of XMLInt32; each array is allocated on demand |
| // if a bit needs to be set in that range |
| // |
| // fMemoryManager |
| // The memory manager used to allocate and deallocate memory |
| // |
| XMLSize_t fArraySize; |
| XMLInt32** fBitArray; |
| MemoryManager* fMemoryManager; |
| }; |
| |
| class CMStateSet : public XMemory |
| { |
| public : |
| // ----------------------------------------------------------------------- |
| // Constructors and Destructor |
| // ----------------------------------------------------------------------- |
| CMStateSet( const XMLSize_t bitCount |
| , MemoryManager* const manager = XMLPlatformUtils::fgMemoryManager) : |
| |
| fBitCount(bitCount) |
| , fDynamicBuffer(0) |
| { |
| // |
| // See if we need to allocate the byte array or whether we can live |
| // within the cached bit high performance scheme. |
| // |
| if (fBitCount > (CMSTATE_CACHED_INT32_SIZE * 32)) |
| { |
| fDynamicBuffer = (CMDynamicBuffer*)manager->allocate(sizeof(CMDynamicBuffer)); |
| fDynamicBuffer->fMemoryManager = manager; |
| // allocate an array of vectors, each one containing CMSTATE_BITFIELD_CHUNK bits |
| fDynamicBuffer->fArraySize = fBitCount / CMSTATE_BITFIELD_CHUNK; |
| if (fBitCount % CMSTATE_BITFIELD_CHUNK) |
| fDynamicBuffer->fArraySize++; |
| fDynamicBuffer->fBitArray = (XMLInt32**) fDynamicBuffer->fMemoryManager->allocate(fDynamicBuffer->fArraySize*sizeof(XMLInt32*)); |
| for(XMLSize_t index = 0; index < fDynamicBuffer->fArraySize; index++) |
| fDynamicBuffer->fBitArray[index]=NULL; |
| } |
| else |
| { |
| for (XMLSize_t index = 0; index < CMSTATE_CACHED_INT32_SIZE; index++) |
| fBits[index] = 0; |
| } |
| } |
| |
| CMStateSet(const CMStateSet& toCopy) : |
| XMemory(toCopy) |
| , fBitCount(toCopy.fBitCount) |
| , fDynamicBuffer(0) |
| { |
| // |
| // See if we need to allocate the byte array or whether we can live |
| // within the cahced bit high performance scheme. |
| // |
| if (fBitCount > (CMSTATE_CACHED_INT32_SIZE * 32)) |
| { |
| fDynamicBuffer = (CMDynamicBuffer*) toCopy.fDynamicBuffer->fMemoryManager->allocate(sizeof(CMDynamicBuffer)); |
| fDynamicBuffer->fMemoryManager = toCopy.fDynamicBuffer->fMemoryManager; |
| fDynamicBuffer->fArraySize = fBitCount / CMSTATE_BITFIELD_CHUNK; |
| if (fBitCount % CMSTATE_BITFIELD_CHUNK) |
| fDynamicBuffer->fArraySize++; |
| fDynamicBuffer->fBitArray = (XMLInt32**) fDynamicBuffer->fMemoryManager->allocate(fDynamicBuffer->fArraySize*sizeof(XMLInt32*)); |
| for(XMLSize_t index = 0; index < fDynamicBuffer->fArraySize; index++) |
| { |
| if(toCopy.fDynamicBuffer->fBitArray[index]!=NULL) |
| { |
| allocateChunk(index); |
| memcpy((void *) fDynamicBuffer->fBitArray[index], |
| (const void *) toCopy.fDynamicBuffer->fBitArray[index], |
| CMSTATE_BITFIELD_INT32_SIZE * sizeof(XMLInt32)); |
| } |
| else |
| fDynamicBuffer->fBitArray[index]=NULL; |
| } |
| } |
| else |
| { |
| memcpy((void *) fBits, |
| (const void *) toCopy.fBits, |
| CMSTATE_CACHED_INT32_SIZE * sizeof(XMLInt32)); |
| } |
| } |
| |
| ~CMStateSet() |
| { |
| if(fDynamicBuffer) |
| { |
| for(XMLSize_t index = 0; index < fDynamicBuffer->fArraySize; index++) |
| if(fDynamicBuffer->fBitArray[index]!=NULL) |
| deallocateChunk(index); |
| fDynamicBuffer->fMemoryManager->deallocate(fDynamicBuffer->fBitArray); |
| fDynamicBuffer->fMemoryManager->deallocate(fDynamicBuffer); |
| } |
| } |
| |
| |
| // ----------------------------------------------------------------------- |
| // Set manipulation methods |
| // ----------------------------------------------------------------------- |
| void operator|=(const CMStateSet& setToOr) |
| { |
| if(fDynamicBuffer==0) |
| { |
| #ifdef XERCES_HAVE_SSE2_INTRINSIC |
| if(XMLPlatformUtils::fgSSE2ok) |
| { |
| __m128i xmm1 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(fBits)); |
| __m128i xmm2 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(setToOr.fBits)); |
| __m128i xmm3 = _mm_or_si128(xmm1, xmm2); // OR 4 32-bit words |
| _mm_storeu_si128(reinterpret_cast<__m128i*>(fBits), xmm3); |
| } |
| else |
| #endif |
| { |
| for (XMLSize_t index = 0; index < CMSTATE_CACHED_INT32_SIZE; index++) |
| if(setToOr.fBits[index]) |
| { |
| if(fBits[index]) |
| fBits[index] |= setToOr.fBits[index]; |
| else |
| fBits[index] = setToOr.fBits[index]; |
| } |
| } |
| } |
| else |
| { |
| for (XMLSize_t index = 0; index < fDynamicBuffer->fArraySize; index++) |
| { |
| XMLInt32 *& other = setToOr.fDynamicBuffer->fBitArray[index]; |
| if(other!=NULL) |
| { |
| // if we haven't allocated the subvector yet, allocate it and copy |
| if(fDynamicBuffer->fBitArray[index]==NULL) |
| { |
| allocateChunk(index); |
| memcpy((void *) fDynamicBuffer->fBitArray[index], |
| (const void *) other, |
| CMSTATE_BITFIELD_INT32_SIZE * sizeof(XMLInt32)); |
| } |
| else |
| { |
| // otherwise, merge them |
| XMLInt32*& mine = fDynamicBuffer->fBitArray[index]; |
| #ifdef XERCES_HAVE_SSE2_INTRINSIC |
| if(XMLPlatformUtils::fgSSE2ok) |
| { |
| for(XMLSize_t subIndex = 0; subIndex < CMSTATE_BITFIELD_INT32_SIZE; subIndex+=4) |
| { |
| __m128i xmm1 = _mm_load_si128(reinterpret_cast<const __m128i*>(&other[subIndex])); |
| __m128i xmm2 = _mm_load_si128(reinterpret_cast<const __m128i*>(&mine[subIndex])); |
| __m128i xmm3 = _mm_or_si128(xmm1, xmm2); // OR 4 32-bit words |
| _mm_store_si128(reinterpret_cast<__m128i*>(&mine[subIndex]), xmm3); |
| } |
| } |
| else |
| #endif |
| { |
| for(XMLSize_t subIndex = 0; subIndex < CMSTATE_BITFIELD_INT32_SIZE; subIndex++) |
| if(setToOr.fDynamicBuffer->fBitArray[index][subIndex]) |
| { |
| if(fDynamicBuffer->fBitArray[index][subIndex]) |
| fDynamicBuffer->fBitArray[index][subIndex] |= setToOr.fDynamicBuffer->fBitArray[index][subIndex]; |
| else |
| fDynamicBuffer->fBitArray[index][subIndex] = setToOr.fDynamicBuffer->fBitArray[index][subIndex]; |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| bool operator==(const CMStateSet& setToCompare) const |
| { |
| if (fBitCount != setToCompare.fBitCount) |
| return false; |
| |
| if(fDynamicBuffer==0) |
| { |
| for (XMLSize_t index = 0; index < CMSTATE_CACHED_INT32_SIZE; index++) |
| { |
| if (fBits[index] != setToCompare.fBits[index]) |
| return false; |
| } |
| } |
| else |
| { |
| for (XMLSize_t index = 0; index < fDynamicBuffer->fArraySize; index++) |
| { |
| XMLInt32 *& other = setToCompare.fDynamicBuffer->fBitArray[index], |
| *& mine = fDynamicBuffer->fBitArray[index]; |
| if(mine==NULL && other==NULL) |
| continue; |
| else if(mine==NULL || other==NULL) // the other should have been empty too |
| return false; |
| else |
| { |
| for(XMLSize_t subIndex = 0; subIndex < CMSTATE_BITFIELD_INT32_SIZE; subIndex++) |
| if(mine[subIndex]!=other[subIndex]) |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| CMStateSet& operator=(const CMStateSet& srcSet) |
| { |
| if (this == &srcSet) |
| return *this; |
| |
| // They have to be the same size |
| if (fBitCount != srcSet.fBitCount) |
| { |
| if(fDynamicBuffer) |
| ThrowXMLwithMemMgr(RuntimeException, XMLExcepts::Bitset_NotEqualSize, fDynamicBuffer->fMemoryManager); |
| else |
| ThrowXML(RuntimeException, XMLExcepts::Bitset_NotEqualSize); |
| } |
| |
| if(fDynamicBuffer==0) |
| { |
| for (XMLSize_t index = 0; index < CMSTATE_CACHED_INT32_SIZE; index++) |
| fBits[index] = srcSet.fBits[index]; |
| } |
| else |
| { |
| for (XMLSize_t index = 0; index < fDynamicBuffer->fArraySize; index++) |
| if(srcSet.fDynamicBuffer->fBitArray[index]==NULL) |
| { |
| // delete this subentry |
| if(fDynamicBuffer->fBitArray[index]!=NULL) |
| deallocateChunk(index); |
| } |
| else |
| { |
| // if we haven't allocated the subvector yet, allocate it and copy |
| if(fDynamicBuffer->fBitArray[index]==NULL) |
| allocateChunk(index); |
| memcpy((void *) fDynamicBuffer->fBitArray[index], |
| (const void *) srcSet.fDynamicBuffer->fBitArray[index], |
| CMSTATE_BITFIELD_INT32_SIZE * sizeof(XMLInt32)); |
| } |
| } |
| return *this; |
| } |
| |
| XMLSize_t getBitCountInRange(XMLSize_t start, XMLSize_t end) const |
| { |
| XMLSize_t count = 0; |
| end /= 32; |
| if(fDynamicBuffer==0) |
| { |
| if(end > CMSTATE_CACHED_INT32_SIZE) |
| end = CMSTATE_CACHED_INT32_SIZE; |
| for (XMLSize_t index = start / 32; index < end; index++) |
| { |
| if (fBits[index] != 0) |
| for(int i=0;i<32;i++) |
| { |
| const XMLInt32 mask = 1UL << i; |
| if(fBits[index] & mask) |
| count++; |
| } |
| } |
| } |
| else |
| { |
| if(end > fDynamicBuffer->fArraySize) |
| end = fDynamicBuffer->fArraySize; |
| for (XMLSize_t index = start / 32; index < end; index++) |
| { |
| if(fDynamicBuffer->fBitArray[index]==NULL) |
| continue; |
| for(XMLSize_t subIndex=0;subIndex < CMSTATE_BITFIELD_INT32_SIZE; subIndex++) |
| { |
| if (fDynamicBuffer->fBitArray[index][subIndex] != 0) |
| for(int i=0;i<32;i++) |
| { |
| const XMLInt32 mask = 1UL << i; |
| if(fDynamicBuffer->fBitArray[index][subIndex] & mask) |
| count++; |
| } |
| } |
| } |
| } |
| return count; |
| } |
| |
| bool getBit(const XMLSize_t bitToGet) const |
| { |
| if (bitToGet >= fBitCount) |
| { |
| if(fDynamicBuffer) |
| ThrowXMLwithMemMgr(ArrayIndexOutOfBoundsException, XMLExcepts::Bitset_BadIndex, fDynamicBuffer->fMemoryManager); |
| else |
| ThrowXML(ArrayIndexOutOfBoundsException, XMLExcepts::Bitset_BadIndex); |
| } |
| |
| // And access the right bit and byte |
| if(fDynamicBuffer==0) |
| { |
| const XMLInt32 mask = 1UL << (bitToGet % 32); |
| const XMLSize_t byteOfs = bitToGet / 32; |
| return (fBits[byteOfs]!=0 && (fBits[byteOfs] & mask) != 0); |
| } |
| else |
| { |
| const XMLSize_t vectorOfs = bitToGet / CMSTATE_BITFIELD_CHUNK; |
| if(fDynamicBuffer->fBitArray[vectorOfs]==NULL) |
| return false; |
| const XMLInt32 mask = 1UL << (bitToGet % 32); |
| const XMLSize_t byteOfs = (bitToGet % CMSTATE_BITFIELD_CHUNK) / 32; |
| return (fDynamicBuffer->fBitArray[vectorOfs][byteOfs]!=0 && (fDynamicBuffer->fBitArray[vectorOfs][byteOfs] & mask) != 0); |
| } |
| } |
| |
| bool isEmpty() const |
| { |
| if(fDynamicBuffer==0) |
| { |
| for (XMLSize_t index = 0; index < CMSTATE_CACHED_INT32_SIZE; index++) |
| { |
| if (fBits[index] != 0) |
| return false; |
| } |
| } |
| else |
| { |
| for (XMLSize_t index = 0; index < fDynamicBuffer->fArraySize; index++) |
| { |
| if(fDynamicBuffer->fBitArray[index]==NULL) |
| continue; |
| for(XMLSize_t subIndex=0;subIndex < CMSTATE_BITFIELD_INT32_SIZE; subIndex++) |
| { |
| if (fDynamicBuffer->fBitArray[index][subIndex] != 0) |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| void setBit(const XMLSize_t bitToSet) |
| { |
| if (bitToSet >= fBitCount) |
| { |
| if(fDynamicBuffer) |
| ThrowXMLwithMemMgr(ArrayIndexOutOfBoundsException, XMLExcepts::Bitset_BadIndex, fDynamicBuffer->fMemoryManager); |
| else |
| ThrowXML(ArrayIndexOutOfBoundsException, XMLExcepts::Bitset_BadIndex); |
| } |
| |
| const XMLInt32 mask = 1UL << (bitToSet % 32); |
| |
| // And access the right bit and byte |
| if(fDynamicBuffer==0) |
| { |
| const XMLSize_t byteOfs = bitToSet / 32; |
| fBits[byteOfs] &= ~mask; |
| fBits[byteOfs] |= mask; |
| } |
| else |
| { |
| const XMLSize_t vectorOfs = bitToSet / CMSTATE_BITFIELD_CHUNK; |
| if(fDynamicBuffer->fBitArray[vectorOfs]==NULL) |
| { |
| allocateChunk(vectorOfs); |
| for(XMLSize_t index=0;index < CMSTATE_BITFIELD_INT32_SIZE; index++) |
| fDynamicBuffer->fBitArray[vectorOfs][index]=0; |
| } |
| const XMLSize_t byteOfs = (bitToSet % CMSTATE_BITFIELD_CHUNK) / 32; |
| fDynamicBuffer->fBitArray[vectorOfs][byteOfs] &= ~mask; |
| fDynamicBuffer->fBitArray[vectorOfs][byteOfs] |= mask; |
| } |
| } |
| |
| void zeroBits() |
| { |
| if(fDynamicBuffer==0) |
| { |
| for (XMLSize_t index = 0; index < CMSTATE_CACHED_INT32_SIZE; index++) |
| fBits[index] = 0; |
| } |
| else |
| { |
| for (XMLSize_t index = 0; index < fDynamicBuffer->fArraySize; index++) |
| // delete this subentry |
| if(fDynamicBuffer->fBitArray[index]!=NULL) |
| deallocateChunk(index); |
| } |
| } |
| |
| XMLSize_t hashCode() const |
| { |
| XMLSize_t hash = 0; |
| if(fDynamicBuffer==0) |
| { |
| for (XMLSize_t index = 0; index<CMSTATE_CACHED_INT32_SIZE; index++) |
| hash = fBits[index] + hash * 31; |
| } |
| else |
| { |
| for (XMLSize_t index = 0; index<fDynamicBuffer->fArraySize; index++) |
| { |
| if(fDynamicBuffer->fBitArray[index]==NULL) |
| // simulates the iteration on the missing bits |
| for(XMLSize_t subIndex=0;subIndex < CMSTATE_BITFIELD_INT32_SIZE; subIndex++) |
| hash *= 31; |
| else |
| for(XMLSize_t subIndex=0;subIndex < CMSTATE_BITFIELD_INT32_SIZE; subIndex++) |
| hash = fDynamicBuffer->fBitArray[index][subIndex] + hash * 31; |
| } |
| } |
| return hash; |
| } |
| |
| private : |
| // ----------------------------------------------------------------------- |
| // Unimplemented constructors and operators |
| // ----------------------------------------------------------------------- |
| CMStateSet(); |
| |
| // ----------------------------------------------------------------------- |
| // Helpers |
| // ----------------------------------------------------------------------- |
| void allocateChunk(const XMLSize_t index) |
| { |
| #ifdef XERCES_HAVE_SSE2_INTRINSIC |
| if(XMLPlatformUtils::fgSSE2ok) |
| fDynamicBuffer->fBitArray[index]=(XMLInt32*)_mm_malloc(CMSTATE_BITFIELD_INT32_SIZE * sizeof(XMLInt32), 16); |
| else |
| #endif |
| fDynamicBuffer->fBitArray[index]=(XMLInt32*)fDynamicBuffer->fMemoryManager->allocate(CMSTATE_BITFIELD_INT32_SIZE * sizeof(XMLInt32)); |
| } |
| |
| void deallocateChunk(const XMLSize_t index) |
| { |
| #ifdef XERCES_HAVE_SSE2_INTRINSIC |
| if(XMLPlatformUtils::fgSSE2ok) |
| _mm_free(fDynamicBuffer->fBitArray[index]); |
| else |
| #endif |
| fDynamicBuffer->fMemoryManager->deallocate(fDynamicBuffer->fBitArray[index]); |
| fDynamicBuffer->fBitArray[index]=NULL; |
| } |
| |
| // ----------------------------------------------------------------------- |
| // Private data members |
| // |
| // fBitCount |
| // The count of bits that the outside world wants to support, |
| // so its the max bit index plus one. |
| // |
| // fBits |
| // When the bit count is less than a threshold (very common), these hold the bits. |
| // Otherwise, the fDynamicBuffer member holds htem. |
| // |
| // fDynamicBuffer |
| // If the bit count is greater than the threshold, then we allocate this structure to |
| // store the bits, the length, and the memory manager to allocate/deallocate |
| // the memory |
| // |
| // ----------------------------------------------------------------------- |
| XMLSize_t fBitCount; |
| XMLInt32 fBits[CMSTATE_CACHED_INT32_SIZE]; |
| CMDynamicBuffer* fDynamicBuffer; |
| |
| friend class CMStateSetEnumerator; |
| }; |
| |
| class CMStateSetEnumerator : public XMemory |
| { |
| public: |
| CMStateSetEnumerator(const CMStateSet* toEnum, XMLSize_t start = 0) : |
| fToEnum(toEnum), |
| fIndexCount((XMLSize_t)-1), |
| fLastValue(0) |
| { |
| // if a starting bit is specified, place fIndexCount at the beginning of the previous 32 bit area |
| // so the findNext moves to the one where 'start' is located |
| if(start > 32) |
| fIndexCount = (start/32 - 1) * 32; |
| findNext(); |
| // if we found data, and fIndexCount is still pointing to the area where 'start' is located, erase the bits before 'start' |
| if(hasMoreElements() && fIndexCount < start) |
| { |
| for(XMLSize_t i=0;i< (start - fIndexCount);i++) |
| { |
| XMLInt32 mask=1UL << i; |
| if(fLastValue & mask) |
| fLastValue &= ~mask; |
| } |
| // in case the 32 bit area contained only bits before 'start', advance |
| if(fLastValue==0) |
| findNext(); |
| } |
| } |
| |
| bool hasMoreElements() |
| { |
| return fLastValue!=0; |
| } |
| |
| unsigned int nextElement() |
| { |
| for(int i=0;i<32;i++) |
| { |
| XMLInt32 mask=1UL << i; |
| if(fLastValue & mask) |
| { |
| fLastValue &= ~mask; |
| unsigned int retVal=(unsigned int)fIndexCount+i; |
| if(fLastValue==0) |
| findNext(); |
| return retVal; |
| } |
| } |
| return 0; |
| } |
| |
| private: |
| void findNext() |
| { |
| if(fToEnum->fDynamicBuffer==0) |
| { |
| XMLSize_t nOffset=((fIndexCount==(XMLSize_t)-1)?0:(fIndexCount/32)+1); |
| for(XMLSize_t index=nOffset;index<CMSTATE_CACHED_INT32_SIZE;index++) |
| { |
| if(fToEnum->fBits[index]!=0) |
| { |
| fIndexCount=index*32; |
| fLastValue=fToEnum->fBits[index]; |
| return; |
| } |
| } |
| } |
| else |
| { |
| XMLSize_t nOffset=((fIndexCount==(XMLSize_t)-1)?0:(fIndexCount/CMSTATE_BITFIELD_CHUNK)); |
| XMLSize_t nSubOffset=((fIndexCount==(XMLSize_t)-1)?0:((fIndexCount % CMSTATE_BITFIELD_CHUNK) /32)+1); |
| for (XMLSize_t index = nOffset; index<fToEnum->fDynamicBuffer->fArraySize; index++) |
| { |
| if(fToEnum->fDynamicBuffer->fBitArray[index]!=NULL) |
| { |
| for(XMLSize_t subIndex=nSubOffset;subIndex < CMSTATE_BITFIELD_INT32_SIZE; subIndex++) |
| if(fToEnum->fDynamicBuffer->fBitArray[index][subIndex]!=0) |
| { |
| fIndexCount=index*CMSTATE_BITFIELD_CHUNK + subIndex*32; |
| fLastValue=fToEnum->fDynamicBuffer->fBitArray[index][subIndex]; |
| return; |
| } |
| } |
| nSubOffset = 0; // next chunks will be processed from the beginning |
| } |
| } |
| } |
| |
| const CMStateSet* fToEnum; |
| XMLSize_t fIndexCount; |
| XMLInt32 fLastValue; |
| }; |
| |
| XERCES_CPP_NAMESPACE_END |
| |
| #endif |