Vault
4.1
|
00001 /* 00002 Copyright c1997-2014 Trygve Isaacson. All rights reserved. 00003 This file is part of the Code Vault version 4.1 00004 http://www.bombaydigital.com/ 00005 License: MIT. See LICENSE.md in the Vault top level directory. 00006 */ 00007 00008 #ifndef vstringiterator_h 00009 #define vstringiterator_h 00010 00013 #include "vtypes.h" 00014 00015 #include "vcodepoint.h" 00016 00017 class VString; 00018 00019 // External linkage so that this header isn't dependent upon vexception.h. 00020 extern void VStringIteratorThrowOutOfBoundsBegin(); 00021 extern void VStringIteratorThrowOutOfBoundsEnd(); 00022 00053 template<typename vstring_ref> // vstring_ref may be 'const VString&' or 'VString&' 00054 class VStringIterator { 00055 00056 public: 00057 VStringIterator(const VStringIterator& iter) 00058 : mSource(iter.mSource) 00059 , mIsForwardIterator(iter.mIsForwardIterator) 00060 , mCurrentCodePointOffset(iter.mCurrentCodePointOffset) 00061 , mSourceLength(iter.mSourceLength) 00062 { 00063 } 00064 00065 VStringIterator(vstring_ref source, bool isForwardIterator, bool goToEnd=false) 00066 : mSource(source) 00067 , mIsForwardIterator(isForwardIterator) 00068 , mCurrentCodePointOffset(0) 00069 , mSourceLength(source.length()) 00070 { 00071 // If going to end on forward iterator, or not going to end on reverse iterator, then seek to end of our data. 00072 if (goToEnd == isForwardIterator) { 00073 this->_seekToEnd(); 00074 } 00075 } 00076 00077 ~VStringIterator() {} 00078 00079 VStringIterator& operator=(const VStringIterator& iter) { 00080 if (this != &iter) { 00081 mSource = iter.mSource; 00082 mIsForwardIterator = iter.mIsForwardIterator; 00083 mCurrentCodePointOffset = iter.mCurrentCodePointOffset; 00084 mSourceLength = iter.mSourceLength; 00085 } 00086 00087 return *this; 00088 } 00089 00090 VCodePoint operator*() const { 00091 if (mIsForwardIterator) { 00092 return VCodePoint(mSource.getDataBufferConst(), mCurrentCodePointOffset); 00093 } else { 00094 int previousCodePointOffset = VCodePoint::getPreviousUTF8CodePointOffset(mSource.getDataBufferConst(), mCurrentCodePointOffset); 00095 return VCodePoint(mSource.getDataBufferConst(), previousCodePointOffset); 00096 } 00097 } 00098 00099 VStringIterator operator+(int n) const { 00100 VStringIterator i(*this); 00101 i += n; 00102 return i; 00103 } 00104 00105 VStringIterator operator-(int n) const { 00106 VStringIterator i(*this); 00107 i -= n; 00108 return i; 00109 } 00110 00111 VStringIterator& operator+=(int n) { 00112 this->_increment(n); 00113 return *this; 00114 } 00115 00116 VStringIterator& operator-=(int n) { 00117 this->_decrement(n); 00118 return *this; 00119 } 00120 00121 VStringIterator& operator++() { 00122 this->_increment(1); 00123 return *this; 00124 } 00125 00126 VStringIterator& operator--() { 00127 this->_decrement(1); 00128 return *this; 00129 } 00130 00131 int getCurrentOffset() const { 00132 return mCurrentCodePointOffset; 00133 } 00134 00135 friend inline bool operator==(const VStringIterator<vstring_ref>& i1, const VStringIterator<vstring_ref>& i2) { 00136 return (i1.mSource == i2.mSource) && (i1.mCurrentCodePointOffset == i2.mCurrentCodePointOffset); 00137 } 00138 00139 friend inline bool operator!=(const VStringIterator<vstring_ref>& i1, const VStringIterator<vstring_ref>& i2) { 00140 return !operator==(i1, i2); 00141 } 00142 00143 private: 00144 00145 void _seekToEnd() { 00146 mCurrentCodePointOffset = mSourceLength; 00147 } 00148 00149 void _increment(int n) { 00150 if (mIsForwardIterator) { 00151 this->_moveOffsetForwardInBuffer(n); 00152 } else { 00153 this->_moveOffsetBackwardInBuffer(n); 00154 } 00155 } 00156 00157 void _decrement(int n) { 00158 if (mIsForwardIterator) { 00159 this->_moveOffsetBackwardInBuffer(n); 00160 } else { 00161 this->_moveOffsetForwardInBuffer(n); 00162 } 00163 } 00164 00165 void _moveOffsetForwardInBuffer(int n) { 00166 // If we are at offset -1 (typical for "end" of reverse iterator), there is no valid buffer to examine at [-1], 00167 // so we just move forward to the first byte, at [0]. After that, normal examine-code-point-and-see-how-big-it-is 00168 // incrementing works. 00169 if ((mCurrentCodePointOffset == -1) && (n > 0)) { 00170 mCurrentCodePointOffset = 0; 00171 --n; 00172 } 00173 00174 for (int i = 0; i < n; ++i) { 00175 if (mCurrentCodePointOffset == mSourceLength) { 00176 VStringIteratorThrowOutOfBoundsEnd(); 00177 } 00178 00179 VCodePoint cp(mSource.getDataBufferConst(), mCurrentCodePointOffset); 00180 mCurrentCodePointOffset += cp.getUTF8Length(); 00181 } 00182 } 00183 00184 void _moveOffsetBackwardInBuffer(int n) { 00185 // If we are at [0], we just move backwards to [-1], and we never move beyond that. (Iterating off the end of 00186 // a container is undefined behavior. At least) 00187 const Vu8* bufferPtr = mSource.getDataBufferConst() + mCurrentCodePointOffset; 00188 for (int i = 0; i < n; ++i) { 00189 00190 do { 00191 if (mCurrentCodePointOffset == 0) { 00192 VStringIteratorThrowOutOfBoundsBegin(); 00193 } 00194 00195 --bufferPtr; 00196 --mCurrentCodePointOffset; 00197 } while (VCodePoint::isUTF8ContinuationByte(*bufferPtr)); 00198 00199 } 00200 } 00201 00202 vstring_ref mSource; 00203 bool mIsForwardIterator; 00204 int mCurrentCodePointOffset; 00205 int mSourceLength; 00206 }; 00207 00208 #endif /* vstringiterator_h */