/* * Copyright 2009-2016 Tilmann Zaeschke. All rights reserved. * * This file is part of ZooDB. * * ZooDB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * ZooDB is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ZooDB. If not, see <http://www.gnu.org/licenses/>. * * See the README and COPYING files for further information. */ package org.zoodb.internal.server.index; import java.util.LinkedList; import java.util.List; import java.util.NoSuchElementException; import org.zoodb.internal.server.DiskIO.PAGE_TYPE; import org.zoodb.internal.server.StorageChannel; import org.zoodb.internal.server.index.LongLongIndex.LLEntryIterator; import org.zoodb.internal.server.index.LongLongIndex.LongLongIterator; import org.zoodb.internal.server.index.LongLongIndex.LongLongUIndex; import org.zoodb.internal.util.CloseableIterator; /** * Index that contains all positions of objects as key. If an object spans multiple pages, * it gets one entry for each page. * The key of each entry is the position. The value of each entry is either the following page * (for multi-page objects) or 0 (for single page objects and for he last entry of a multi-page * object). * * See also PagedOidIndex. * * @author Tilmann Zaeschke * */ public class PagedPosIndex { public static final long MARK_SECONDARY = 0x00000000FFFFFFFFL; public static class ObjectPosIteratorMerger implements CloseableIterator<Long> { private final LinkedList<ObjectPosIterator> il = new LinkedList<PagedPosIndex.ObjectPosIterator>(); private ObjectPosIterator iter = null; void add(ObjectPosIterator iter) { if (this.iter == null) { this.iter = iter; } else { il.add(iter); } } @Override public boolean hasNext() { throw new UnsupportedOperationException(); } public boolean hasNextOPI() { if (iter == null) { return false; } if (iter.hasNextOPI()) { return true; } while (!il.isEmpty()) { iter.close(); iter = il.removeFirst(); if (iter.hasNext){ return true; } } return false; } @Override public Long next() { throw new UnsupportedOperationException(); } public long nextPos() { if (!hasNextOPI()) { throw new NoSuchElementException(); } return iter.nextPos(); } @Override public void remove() { throw new UnsupportedOperationException(); } @Override public void close() { if (iter != null) { iter.close(); iter = null; } for (ObjectPosIterator i: il) { i.close(); } il.clear(); } } /** * This iterator returns only start-pages of objects and skips all intermediate pages. * * @author Tilmann Zaeschke */ public static class ObjectPosIterator implements CloseableIterator<Long> { private LLEntryIterator iter; private boolean hasNext = true; private long nextPos = -1; public ObjectPosIterator(LongLongUIndex root, long minKey, long maxKey) { iter = root.iterator(minKey, maxKey); nextPos(); } @Override public boolean hasNext() { return hasNextOPI(); } public boolean hasNextOPI() { return hasNext; } @Override /** * This next() method returns only primary pages. */ public Long next() { return nextPos(); } public long nextPos() { //we always only need the key, we return only a long-key long ret = nextPos; if (!iter.hasNextULL()) { //close iterator hasNext = false; iter.close(); return ret; } //How do we recognize the next object starting point? //The offset of the key is MARK_SECONDARY. nextPos = iter.nextKey(); while (iter.hasNextULL() && BitTools.getOffs(nextPos) == (int)MARK_SECONDARY) { nextPos = iter.nextKey(); } if (BitTools.getOffs(nextPos) == (int)MARK_SECONDARY) { //close iterator hasNext = false; iter.close(); return ret; } return ret; } @Override public void remove() { //iter.remove(); throw new UnsupportedOperationException(); } @Override public void close() { iter.close(); hasNext = false; } } private transient LongLongUIndex idx; /** * Constructor for creating new index. * @param file */ public PagedPosIndex(StorageChannel file) { //8 bit starting pos, 4 bit following page idx = IndexFactory.createUniqueIndex(PAGE_TYPE.POS_INDEX, file, 8, 4); } /** * Constructor for reading index from disk. */ private PagedPosIndex(StorageChannel file, int pageId) { //8 bit starting pos, 4 bit following page idx = IndexFactory.loadUniqueIndex(PAGE_TYPE.POS_INDEX, file, pageId, 8, 4); } /** * Constructor for creating new index. * @param file */ public static PagedPosIndex newIndex(StorageChannel file) { return new PagedPosIndex(file); } /** * Constructor for reading index from disk. * @param pageId Set this to MARK_SECONDARY to indicate secondary pages. */ public static PagedPosIndex loadIndex(StorageChannel file, int pageId) { return new PagedPosIndex(file, pageId); } /** * * @param page * @param offs (long)! To avoid problems when casting -1 from int to long. * @param nextPage */ public void addPos(int page, long offs, int nextPage) { long newKey = (((long)page) << 32) | (long)offs; idx.insertLong(newKey, nextPage); } public long removePosLong(long pos) { return idx.removeLong(pos); } public ObjectPosIterator iteratorObjects() { return new ObjectPosIterator(idx, 0, Long.MAX_VALUE); } public LongLongIterator<LongLongIndex.LLEntry> iteratorPositions() { return idx.iterator(0, Long.MAX_VALUE); } public void print() { idx.print(); } public long getMaxValue() { return idx.getMaxKey(); } public int statsGetLeavesN() { return idx.statsGetLeavesN(); } public int statsGetInnerN() { return idx.statsGetInnerN(); } public int write() { return idx.write(); } public long removePosLongAndCheck(long pos) { long min = BitTools.getMinPosInPage(pos); long max = BitTools.getMaxPosInPage(pos); return idx.deleteAndCheckRangeEmpty(pos, min, max) << 32; } public List<Integer> debugPageIds() { return idx.debugPageIds(); } public void clear() { idx.clear(); } public long size() { return idx.size(); } }