001: /*
002: Copyright (C) 2007 Mobixess Inc. http://www.java-objects-database.com
003:
004: This file is part of the JODB (Java Objects Database) open source project.
005:
006: JODB is free software; you can redistribute it and/or modify it under
007: the terms of version 2 of the GNU General Public License as published
008: by the Free Software Foundation.
009:
010: JODB is distributed in the hope that it will be useful, but WITHOUT ANY
011: WARRANTY; without even the implied warranty of MERCHANTABILITY or
012: FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
013: for more details.
014:
015: You should have received a copy of the GNU General Public License along
016: with this program; if not, write to the Free Software Foundation, Inc.,
017: 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
018: */
019: package com.mobixess.jodb.core.index;
020:
021: import java.io.IOException;
022: import java.lang.reflect.Field;
023: import java.nio.ByteBuffer;
024: import java.util.ConcurrentModificationException;
025: import java.util.WeakHashMap;
026:
027: import com.mobixess.jodb.core.IllegalClassTypeException;
028: import com.mobixess.jodb.core.JODBTransient;
029: import com.mobixess.jodb.core.JodbIOException;
030: import com.mobixess.jodb.core.agent.JODBAgent;
031: import com.mobixess.jodb.core.io.JODBOperationContext;
032: import com.mobixess.jodb.core.transaction.TransactionContainer;
033: import com.mobixess.jodb.util.PrimitiveJavaTypesUtil;
034: import com.mobixess.jodb.util.PrimitiveJavaTypesUtil.PRIMITIVES_ENUMERATION;
035:
036: public class JODBIndexingAgent extends JODBAgent {
037:
038: private final static int CHUNK_HOLDER_DEFAULT_CAPACITY = 1000;
039: private final static int CHUNK_HOLDER_DEFAULT_INCREMENT = 500;
040: private final static int CHUNK_CAPACITY = 1000;
041: private final static double PREFERRED_CHUNK_LOAD_FACTOR = 0.75;
042:
043: private final static boolean DEEP_SANITY_CHECK = false;
044:
045: private int _fieldId;
046: private int _fieldTypeEnumIndex;
047: private int _classId;
048: private int _dataElementSize;
049: private long[][] _offsetArrays;
050: private byte[][] _dataArrays;
051: private int[] _totalIndexesWithinChunks;
052: private int _totalChunks;
053: private int _totalIndexes;
054: @JODBTransient
055: private boolean _activated;
056: @JODBTransient
057: private WeakHashMap<long[], ByteBuffer> _chunksWrappersCache;
058: @JODBTransient
059: private PRIMITIVES_ENUMERATION _fieldTypeEnum;
060:
061: public JODBIndexingAgent() {
062: }
063:
064: /**
065: * @param fieldId
066: * @throws JodbIOException
067: */
068: public JODBIndexingAgent(Field field, JODBOperationContext context)
069: throws IOException {
070: super ();
071: if (field == null || context == null) {
072: throw new NullPointerException();
073: }
074: _classId = context.getBase().getOrSetClassTypeSubstitutionID(
075: field.getDeclaringClass());
076: _fieldId = context.getBase().getOrSetFieldSubstitutionID(field);
077: _fieldTypeEnum = PrimitiveJavaTypesUtil.getEnumeratedType(field
078: .getType().getName());
079: _fieldTypeEnumIndex = _fieldTypeEnum.ordinal();
080: _totalIndexesWithinChunks = new int[CHUNK_HOLDER_DEFAULT_CAPACITY];
081: _offsetArrays = new long[CHUNK_HOLDER_DEFAULT_CAPACITY][];
082: _offsetArrays[0] = new long[CHUNK_CAPACITY];
083: _totalChunks = 1;
084: Class type = field.getType();
085: if (type.isPrimitive()) {
086: try {
087: _dataElementSize = PrimitiveJavaTypesUtil
088: .getDataOutputWriteLen(type);
089: _dataArrays = new byte[CHUNK_HOLDER_DEFAULT_CAPACITY][];
090: _dataArrays[0] = new byte[CHUNK_CAPACITY
091: * _dataElementSize];
092: } catch (JodbIOException e) {
093: // TODO log
094: e.printStackTrace();
095: }
096: }
097: _activated = true;
098: }
099:
100: private synchronized void checkIfActivated(
101: JODBOperationContext context) throws IOException {
102: if (_activated) {
103: return;
104: }
105: context.getSession().activate(this , Integer.MAX_VALUE);
106: _activated = true;
107: }
108:
109: private PRIMITIVES_ENUMERATION getFieldTypeEnum() {
110: if (_fieldTypeEnum == null) {
111: _fieldTypeEnum = PRIMITIVES_ENUMERATION.values()[_fieldTypeEnumIndex];
112: }
113: return _fieldTypeEnum;
114: }
115:
116: private void sanityCheck() {
117: int indexesInChunks = 0;
118: for (int i = 0; i < _totalChunks; i++) {
119: indexesInChunks += _totalIndexesWithinChunks[i];
120: }
121: if (indexesInChunks != _totalIndexes) {
122: throw new RuntimeException(
123: "Sanity check failed indexesInChunks!=_totalIndexes");
124: }
125:
126: for (int i = 0; i < _totalChunks; i++) {
127: for (int j = 0; j < _totalIndexesWithinChunks[i]; j++) {
128: int nextChunk = i, nextIndex = j + 1;
129: if (nextIndex == _totalIndexesWithinChunks[i]) {
130: nextChunk++;
131: nextIndex = 0;
132: }
133: checkOffsetNotExists(_offsetArrays[i][j], nextChunk,
134: nextIndex);
135: }
136: }
137: /*
138: for (int i = 0; i < _totalChunks; i++) {
139: for (int j = 0; j < _totalIndexesWithinChunks[i]; j++) {
140: int nextChunk = i, nextIndex = j+1;
141: if(nextIndex == _totalIndexesWithinChunks[i]){
142: nextChunk++;
143: nextIndex = 0;
144: }
145: checkOffsetNotExists(_offsetArrays[i][j],nextChunk,nextIndex);
146: }
147: }
148: */
149: for (int i = 0; i < _totalIndexes - 1; i++) {
150: ByteBuffer first = getValueForIndex(i, true);
151: ByteBuffer next = getValueForIndex(i + 1, true);
152: try {
153: int compareResult = PrimitiveJavaTypesUtil
154: .comparePrimitives(first, getFieldTypeEnum(),
155: next);
156: if (compareResult > 0) {
157: throw new RuntimeException();
158: }
159: } catch (IOException e) {
160: // TODO Auto-generated catch block
161: e.printStackTrace();
162: }
163: // if(first.compareTo(next)>0){
164: // throw new RuntimeException();
165: // }
166: }
167: }
168:
169: private void checkOffsetNotExists(long offset, int startChunk,
170: int startOffsetWithinChunk) {
171: for (int i = startChunk; i < _totalChunks; i++) {
172: for (int j = startOffsetWithinChunk; j < _totalIndexesWithinChunks[i]; j++) {
173: if (offset == _offsetArrays[i][j]) {
174: throw new RuntimeException();
175: }
176: }
177: }
178: }
179:
180: public int getFieldId() {
181: return _fieldId;
182: }
183:
184: public int getClassId() {
185: return _classId;
186: }
187:
188: public synchronized void insertIndex(long offsetId,
189: ByteBuffer value, JODBOperationContext context)
190: throws IOException {
191: // if(DEEP_SANITY_CHECK){
192: // sanityCheck();
193: // }
194: int insertionIndex = searchIndex(value);
195: if (insertionIndex < 0) {
196: insertionIndex = -insertionIndex - 1;
197: }
198: long chunkIndexAndChunkStart = getChunkForIndex(insertionIndex);
199: int chunkIndex = (int) ((chunkIndexAndChunkStart >> 32) & 0xFFFFFFFF);
200: int chunkStart = (int) (chunkIndexAndChunkStart & 0xFFFFFFFF);
201: if (chunkIndex == _totalChunks) {
202: insertChunk(chunkIndex, context);
203: }
204: int indexInChunk = insertionIndex - chunkStart;
205: reserveSpace(chunkIndex, indexInChunk, context);
206: _offsetArrays[chunkIndex][indexInChunk] = offsetId;
207: if (value != null) {
208: value.get(_dataArrays[chunkIndex], indexInChunk
209: * _dataElementSize, _dataElementSize);
210: }
211: // if(DEEP_SANITY_CHECK){
212: // sanityCheck();
213: // }
214:
215: }
216:
217: public synchronized boolean removeIndex(long objectId,
218: ByteBuffer value, JODBOperationContext context)
219: throws IOException {
220: // if(DEEP_SANITY_CHECK){
221: // sanityCheck();
222: // }
223: long compositeIndex = searchIndex(objectId, value);
224: if (compositeIndex == -1) {
225: if (DEEP_SANITY_CHECK) {
226: sanityCheck();
227: }
228: compositeIndex = searchIndex(objectId, value);
229: return false;
230: }
231: int chunkIndex = (int) ((compositeIndex >> 32) & 0xFFFFFFFF);
232: int indexInChunk = (int) (compositeIndex & 0xFFFFFFFF);
233: long[] offsetsChunk = _offsetArrays[chunkIndex];
234: System.arraycopy(offsetsChunk, indexInChunk + 1, offsetsChunk,
235: indexInChunk, _totalIndexesWithinChunks[chunkIndex]
236: - indexInChunk - 1);
237: byte[] dataChunks = _dataArrays[chunkIndex];
238: System
239: .arraycopy(dataChunks, (indexInChunk + 1)
240: * _dataElementSize, dataChunks, indexInChunk
241: * _dataElementSize,
242: (_totalIndexesWithinChunks[chunkIndex]
243: - indexInChunk - 1)
244: * _dataElementSize);
245: _totalIndexesWithinChunks[chunkIndex]--;
246: _totalIndexes--;
247: // if(DEEP_SANITY_CHECK){
248: // sanityCheck();
249: // }
250: return true;
251: }
252:
253: private void reserveSpace(int chunkIndex, int indexInChunk,
254: JODBOperationContext context) {
255: long[] currentOffsetsChunk = _offsetArrays[chunkIndex];
256: int chunkLen = _offsetArrays[chunkIndex].length;
257: if (_totalIndexesWithinChunks[chunkIndex] == chunkLen) {
258: //move indexes to next chunk or split
259: int indexesToMove = Math.min(
260: (int) (chunkLen * PREFERRED_CHUNK_LOAD_FACTOR),
261: chunkLen - indexInChunk);
262: if (chunkIndex == _totalChunks - 1
263: || (_offsetArrays[chunkIndex + 1].length - _totalIndexesWithinChunks[chunkIndex + 1]) <= indexesToMove) {
264: insertChunk(chunkIndex + 1, context);
265: indexesToMove = Math.min((int) (chunkLen * 0.5),
266: chunkLen - indexInChunk);
267: }
268: long[] nextOffsetsChunk = _offsetArrays[chunkIndex + 1];
269: //make sure next chunk have space
270: System.arraycopy(nextOffsetsChunk, 0, nextOffsetsChunk,
271: indexesToMove,
272: _totalIndexesWithinChunks[chunkIndex + 1]);
273: if (_dataArrays != null) {
274: byte[] nexDataArrayChunk = _dataArrays[chunkIndex + 1];
275: System.arraycopy(nexDataArrayChunk, 0,
276: nexDataArrayChunk, indexesToMove
277: * _dataElementSize,
278: _totalIndexesWithinChunks[chunkIndex + 1]
279: * _dataElementSize);
280: }
281: //move data
282: int srcIndex = _totalIndexesWithinChunks[chunkIndex]
283: - indexesToMove;
284: System.arraycopy(currentOffsetsChunk, srcIndex,
285: nextOffsetsChunk, 0, indexesToMove);
286: if (_dataArrays != null) {
287: byte[] nexDataArrayChunk = _dataArrays[chunkIndex + 1];
288: byte[] currentDataArrayChunk = _dataArrays[chunkIndex];
289: System.arraycopy(currentDataArrayChunk, srcIndex
290: * _dataElementSize, nexDataArrayChunk, 0,
291: indexesToMove * _dataElementSize);
292: }
293: _totalIndexesWithinChunks[chunkIndex] = srcIndex;//new chunk length
294: _totalIndexesWithinChunks[chunkIndex + 1] = _totalIndexesWithinChunks[chunkIndex + 1]
295: + indexesToMove;
296: }
297: //space is guaranteed from here
298: System.arraycopy(currentOffsetsChunk, indexInChunk,
299: currentOffsetsChunk, indexInChunk + 1,
300: _totalIndexesWithinChunks[chunkIndex] - indexInChunk);
301: if (_dataArrays != null) {
302: byte[] currentDataArrayChunk = _dataArrays[chunkIndex];
303: System
304: .arraycopy(
305: currentDataArrayChunk,
306: indexInChunk * _dataElementSize,
307: currentDataArrayChunk,
308: (indexInChunk + 1) * _dataElementSize,
309: (_totalIndexesWithinChunks[chunkIndex] - indexInChunk)
310: * _dataElementSize);
311: }
312: _totalIndexesWithinChunks[chunkIndex]++;
313: _totalIndexes++;
314: }
315:
316: /**
317: *
318: * @param index
319: * @return -1 means need chunk append
320: */
321: public final long getChunkForIndex(int index) {
322: int chunkIndex = -1;
323: int totalInPreviousChunks = 0;
324: int currentChunkStart = 0;
325: do {
326: chunkIndex++;
327: currentChunkStart = totalInPreviousChunks;
328: totalInPreviousChunks += _totalIndexesWithinChunks[chunkIndex];
329: } while (totalInPreviousChunks < index + 1
330: && chunkIndex < _totalChunks - 1);
331: if (totalInPreviousChunks < index + 1
332: && _totalIndexesWithinChunks[chunkIndex] == _offsetArrays[chunkIndex].length) {
333: currentChunkStart = totalInPreviousChunks;
334: chunkIndex++;
335: }
336: return (long) chunkIndex << 32 | (long) currentChunkStart;
337: }
338:
339: private ByteBuffer getValueForIndex(int index) {
340: return getValueForIndex(index, false);
341: }
342:
343: private ByteBuffer getValueForIndex(int index,
344: boolean newChunkWrapper) {
345: long compositeIndex = getChunkForIndex(index);
346: int chunkIndex = (int) ((compositeIndex >> 32) & 0xFFFFFFFF);
347: int chunkStart = (int) (compositeIndex & 0xFFFFFFFF);
348: int indexInChunk = index - chunkStart;
349: return getValueForIndex(chunkIndex, indexInChunk,
350: newChunkWrapper);
351: }
352:
353: private ByteBuffer getValueForIndex(int chunkIndex,
354: int offsetIndexInChunk) {
355: return getValueForIndex(chunkIndex, offsetIndexInChunk, false);
356: }
357:
358: private ByteBuffer getValueForIndex(int chunkIndex,
359: int offsetIndexInChunk, boolean newByteBuffer) {
360: ByteBuffer byteBuffer;
361: if (!newByteBuffer) {
362: byteBuffer = getWrapperForChunk(chunkIndex);
363: } else {
364: byteBuffer = ByteBuffer.wrap(_dataArrays[chunkIndex]);
365: }
366: getValueForIndex(chunkIndex, offsetIndexInChunk, byteBuffer);
367: return byteBuffer;
368: }
369:
370: private void getValueForIndex(int chunkIndex,
371: int offsetIndexInChunk, ByteBuffer chunkWrapper) {
372: int indexInChunk = offsetIndexInChunk * _dataElementSize;
373: chunkWrapper.limit(indexInChunk + _dataElementSize);
374: chunkWrapper.position(indexInChunk);
375: }
376:
377: private ByteBuffer getWrapperForChunk(int chunkIndex) {
378: if (_chunksWrappersCache == null) {
379: _chunksWrappersCache = new WeakHashMap<long[], ByteBuffer>();
380: }
381: ByteBuffer result = _chunksWrappersCache
382: .get(_offsetArrays[chunkIndex]);
383: if (result == null) {
384: result = ByteBuffer.wrap(_dataArrays[chunkIndex]);
385: _chunksWrappersCache.put(_offsetArrays[chunkIndex], result);
386: }
387: return result;
388: }
389:
390: private int linearValueSearch(ByteBuffer valueKey) {
391: for (int i = 0; i < _totalIndexes; i++) {
392: ByteBuffer array_key = getValueForIndex(i);
393: if (valueKey.equals(array_key)) {
394: return i;
395: }
396: }
397: return -1;
398: }
399:
400: public int linearIdSearch(long id) {
401: int index = 0;
402: for (int i = 0; i < _totalChunks; i++) {
403: for (int j = 0; j < _totalIndexesWithinChunks[i]; j++) {
404: if (id == _offsetArrays[i][j]) {
405: return index;
406: }
407: index++;
408: }
409: }
410: return -1;
411: }
412:
413: /**
414: *
415: * @param objectId
416: * @param valueKey
417: * @return "chunk index" << 32 | "index in chunk"
418: * @throws IOException
419: */
420: private long searchIndex(long objectId, ByteBuffer valueKey)
421: throws IOException {
422: int index = binarySearch(valueKey);
423: // if(objectId == 45001){
424: // index = linearValueSearch(valueKey);
425: // System.err.println("linear ID search "+linearIdSearch(objectId));
426: // }
427: long compositeIndex = getChunkForIndex(index);
428: int chunkIndexForward, chunkIndexBackward;
429: chunkIndexForward = chunkIndexBackward = (int) ((compositeIndex >> 32) & 0xFFFFFFFF);
430: int chunkStartForward, chunkStartBackward;
431: chunkStartForward = chunkStartBackward = (int) (compositeIndex & 0xFFFFFFFF);
432: int indexInChunkBackward = index - chunkStartBackward;
433: int indexInChunkForward = indexInChunkBackward + 1;
434: boolean continueBackward = true;
435:
436: while (continueBackward) {
437: if (_offsetArrays[chunkIndexBackward][indexInChunkBackward] == objectId) {
438: return (long) chunkIndexBackward << 32
439: | indexInChunkBackward;
440: }
441: indexInChunkBackward--;
442: if (indexInChunkBackward < 0) {
443: chunkStartBackward -= _totalIndexesWithinChunks[chunkIndexBackward];//move chunk start to previous chunk start
444: chunkIndexBackward--;
445: if (chunkIndexBackward < 0) {
446: break;
447: }
448: indexInChunkBackward = _totalIndexesWithinChunks[chunkIndexBackward] - 1;
449: }
450: ByteBuffer nextValue = getValueForIndex(chunkIndexBackward,
451: indexInChunkBackward);
452: continueBackward = valueKey.equals(nextValue);
453: }
454:
455: boolean continueForward = true;
456:
457: while (continueForward) {
458: if (indexInChunkForward >= _totalIndexesWithinChunks[chunkIndexForward]) {
459: chunkStartForward += _totalIndexesWithinChunks[chunkIndexForward];//move chunk end to next chunk start
460: chunkIndexForward++;
461: if (chunkIndexForward >= _totalChunks) {
462: break;
463: }
464: indexInChunkForward = 0;
465: }
466: if (_offsetArrays[chunkIndexForward][indexInChunkForward] == objectId) {
467: return (long) chunkIndexForward << 32
468: | indexInChunkForward;
469: }
470: ByteBuffer nextValue = getValueForIndex(chunkIndexForward,
471: indexInChunkForward);
472: continueForward = valueKey.equals(nextValue);
473: indexInChunkForward++;
474: }
475: return -1;
476: }
477:
478: public int searchIndex(ByteBuffer key) throws IOException {
479: return binarySearch(key);
480: }
481:
482: public int binarySearch(ByteBuffer key) throws IOException {
483: int fromIndex = 0, toIndex = _totalIndexes;
484: int low = fromIndex;
485: int high = toIndex - 1;
486: while (low <= high) {
487: int mid = (low + high) >> 1;
488: ByteBuffer array_key = getValueForIndex(mid);
489: int result = -PrimitiveJavaTypesUtil.comparePrimitives(
490: array_key, array_key.position(),
491: getFieldTypeEnum(), key); //compareTo(key, array_key);//key.compareTo(array_key);// c.compare( key, array_key );
492: if (result < 0) {
493: high = mid - 1;
494: } else if (result > 0) {
495: low = mid + 1;
496: } else {
497: return mid;
498: }
499: }
500:
501: return -(low + 1);
502: }
503:
504: /**
505: * Compares this buffer to another.
506: *
507: * <p> Two byte buffers are compared by comparing their sequences of
508: * remaining elements lexicographically, without regard to the starting
509: * position of each sequence within its corresponding buffer.
510: *
511: * <p> A byte buffer is not comparable to any other type of object.
512: *
513: * @return A negative integer, zero, or a positive integer as this buffer
514: * is less than, equal to, or greater than the given buffer
515: * @throws IOException
516: */
517: public int compareTo(ByteBuffer this Key, ByteBuffer that)
518: throws IOException {
519: return PrimitiveJavaTypesUtil.comparePrimitives(this Key, 0,
520: getFieldTypeEnum(), that);
521: /*int n = thisKey.position() + Math.min(thisKey.remaining(), that.remaining());
522: for (int i = thisKey.position(), j = that.position(); i < n; i++, j++) {
523: byte v1 = thisKey.get(i);
524: byte v2 = that.get(j);
525: if (v1 == v2)
526: continue;
527: if ((v1 != v1) && (v2 != v2)) // For float and double
528: continue;
529: if (v1 < v2)
530: return -1;
531: return +1;
532: }
533: return thisKey.remaining() - that.remaining();*/
534: }
535:
536: private synchronized void insertChunk(int index,
537: JODBOperationContext context) {
538: TransactionContainer transactionContainer = context != null ? context
539: .getTransactionContainer()
540: : null;
541:
542: _offsetArrays = JODBIndexingRootAgent
543: .ensurePersistentArrayCapacity(_offsetArrays,
544: _totalChunks + 1, transactionContainer,
545: CHUNK_HOLDER_DEFAULT_CAPACITY);
546: if (index != _totalChunks) {
547: System.arraycopy(_offsetArrays, index, _offsetArrays,
548: index + 1, _totalChunks - index);
549: System.arraycopy(_totalIndexesWithinChunks, index,
550: _totalIndexesWithinChunks, index + 1, _totalChunks
551: - index);
552: }
553: _offsetArrays[index] = new long[CHUNK_CAPACITY];
554: if (transactionContainer != null) {//make sure the new array is registered in transaction container
555: try {
556: transactionContainer.set(_offsetArrays,
557: Integer.MAX_VALUE);
558: } catch (Exception e) {
559: e.printStackTrace();
560: }
561: }
562: _totalIndexesWithinChunks[index] = 0;
563: if (_dataArrays != null) {
564: _dataArrays = JODBIndexingRootAgent
565: .ensurePersistentArrayCapacity(_dataArrays,
566: _totalChunks + 1, transactionContainer,
567: CHUNK_HOLDER_DEFAULT_CAPACITY);
568: if (index != _totalChunks) {
569: System.arraycopy(_dataArrays, index, _dataArrays,
570: index + 1, _totalChunks - index);
571: }
572: _dataArrays[index] = new byte[CHUNK_CAPACITY
573: * _dataElementSize];
574: if (transactionContainer != null) {//make sure the new array is registered in transaction container
575: try {
576: transactionContainer.set(_dataArrays,
577: Integer.MAX_VALUE);
578: } catch (Exception e) {
579: e.printStackTrace();
580: }
581: }
582: }
583: _totalChunks++;
584: }
585:
586: public IndexDataIterator getIndexIterator(boolean isForwardIterator) {
587: return isForwardIterator ? new ForwardIndexIteratorImpl()
588: : new BackwardIndexIteratorImpl();
589: }
590:
591: class ForwardIndexIteratorImpl implements IndexDataIterator {
592:
593: int _initialTotalIndexes;
594: int _indexesLeft;
595: int _chunkOffset;
596: int _offsetInChunk;
597:
598: public ForwardIndexIteratorImpl() {
599: _initialTotalIndexes = _indexesLeft = _totalIndexes;
600: _chunkOffset = 0;
601: _offsetInChunk = -1;
602: }
603:
604: public boolean hasNext() {
605: return _indexesLeft > 0;
606: }
607:
608: public int length() {
609: return _initialTotalIndexes;
610: }
611:
612: public long next(ByteBuffer result) {
613: if (_indexesLeft <= 0) {
614: throw new IllegalStateException();
615: }
616: if (_initialTotalIndexes != _totalIndexes) {
617: throw new ConcurrentModificationException();
618: }
619: _offsetInChunk++;
620: while (_offsetInChunk >= _totalIndexesWithinChunks[_chunkOffset]) {
621: _chunkOffset++;
622: _offsetInChunk = 0;
623: }
624: _indexesLeft--;
625: if (result != null) {
626: byte[] src = _dataArrays[_chunkOffset];
627: result.put(src, _offsetInChunk * _dataElementSize,
628: _dataElementSize);
629: }
630: return _offsetArrays[_chunkOffset][_offsetInChunk];
631: }
632:
633: public long next() {
634: return next(null);
635: }
636:
637: }
638:
639: class BackwardIndexIteratorImpl implements IndexDataIterator {
640:
641: int _initialTotalIndexes;
642: int _indexesLeft;
643: int _chunkOffset;
644: int _offsetInChunk;
645:
646: public BackwardIndexIteratorImpl() {
647: _initialTotalIndexes = _indexesLeft = _totalIndexes;
648: _chunkOffset = _totalChunks - 1;
649: _offsetInChunk = _totalIndexesWithinChunks[_chunkOffset];
650: }
651:
652: public boolean hasNext() {
653: return _indexesLeft > 0;
654: }
655:
656: public int length() {
657: return _initialTotalIndexes;
658: }
659:
660: public long next(ByteBuffer result) {
661: if (_indexesLeft <= 0) {
662: throw new IllegalStateException();
663: }
664: if (_initialTotalIndexes != _totalIndexes) {
665: throw new ConcurrentModificationException();
666: }
667: _offsetInChunk--;
668: while (_offsetInChunk < 0
669: || _totalIndexesWithinChunks[_chunkOffset] <= 0) {
670: _chunkOffset--;
671: _offsetInChunk = _totalIndexesWithinChunks[_chunkOffset] - 1;
672: }
673: _indexesLeft--;
674: if (result != null) {
675: byte[] src = _dataArrays[_chunkOffset];
676: result.put(src, _offsetInChunk * _dataElementSize,
677: _dataElementSize);
678: }
679: return _offsetArrays[_chunkOffset][_offsetInChunk];
680: }
681:
682: public long next() {
683: return next(null);
684: }
685:
686: }
687: }
|