package com.ctriposs.tsdb.iterator; import java.io.IOException; import java.util.Map.Entry; import com.ctriposs.tsdb.DBConfig; import com.ctriposs.tsdb.InternalEntry; import com.ctriposs.tsdb.InternalKey; import com.ctriposs.tsdb.common.IFileIterator; import com.ctriposs.tsdb.common.IStorage; import com.ctriposs.tsdb.storage.CodeBlock; import com.ctriposs.tsdb.storage.CodeItem; import com.ctriposs.tsdb.storage.Head; import com.ctriposs.tsdb.storage.TimeBlock; import com.ctriposs.tsdb.storage.TimeItem; public class FileSeekIterator implements IFileIterator<InternalKey, byte[]> { private IStorage storage; private int maxCodeBlockIndex = -2; private int maxTimeBlockIndex = -2; private int curCodeBlockIndex = -1; private int curTimeBlockIndex = -1; private Entry<InternalKey, byte[]> curEntry; private TimeBlock curTimeBlock; private CodeBlock curCodeBlock; private CodeItem curCodeItem; private Head head; private long fileNumber; public FileSeekIterator(IStorage storage)throws IOException { this.storage = storage; byte[] bytes = new byte[Head.HEAD_SIZE]; this.storage.get(0, bytes); this.head = new Head(bytes); this.maxCodeBlockIndex = (head.getCodeCount() + DBConfig.BLOCK_MAX_COUNT-1)/DBConfig.BLOCK_MAX_COUNT - 1; nextCodeBlock(); this.curCodeItem = curCodeBlock.current(); this.curEntry = null; this.curTimeBlock = null; } public FileSeekIterator(IStorage storage, long fileNumber)throws IOException { this(storage); this.fileNumber = fileNumber; } @Override public boolean hasNext() { if(curTimeBlockIndex <= maxTimeBlockIndex){ if(curTimeBlock != null){ if(!curTimeBlock.hasNext()){ try{ nextTimeBlock(); if(curTimeBlock == null){ return false; }else{ if(curCodeItem != null){ readEntry(curCodeItem.getCode(), curTimeBlock.current(), true); return true; }else{ return false; } } }catch(IOException e){ throw new RuntimeException(e); } }else{ return true; } }else{ try{ nextTimeBlock(); if(curTimeBlock == null){ return false; }else{ if(curCodeItem != null){ readEntry(curCodeItem.getCode(), curTimeBlock.current(), true); return true; }else{ return false; } } }catch(IOException e){ throw new RuntimeException(e); } } } return false; } @Override public boolean hasPrev() { if(curTimeBlockIndex >= 0){ if(curTimeBlock != null){ if(!curTimeBlock.hasPrev()){ try{ prevTimeBlock(); if(curTimeBlock == null){ return false; }else{ if(curCodeItem != null){ readEntry(curCodeItem.getCode(), curTimeBlock.last(), true); return true; }else{ return false; } } }catch(IOException e){ throw new RuntimeException(e); } }else{ return true; } }else{ try{ prevTimeBlock(); if(curTimeBlock == null){ return false; }else{ if(curCodeItem != null){ readEntry(curCodeItem.getCode(), curTimeBlock.last(), true); return true; }else{ return false; } } }catch(IOException e){ throw new RuntimeException(e); } } } return false; } @Override public boolean hasNextCode() throws IOException { if(curCodeBlockIndex <= maxCodeBlockIndex){ if(curCodeBlock != null){ if(!curCodeBlock.hasNext()){ try{ nextCodeBlock(); }catch(IOException e){ throw new RuntimeException(e); } if(curCodeBlock == null){ return false; }else{ return true; } }else{ return true; } }else{ try{ nextCodeBlock(); }catch(IOException e){ throw new RuntimeException(e); } if(curCodeBlock == null){ return false; }else{ return true; } } } return false; } @Override public boolean hasPrevCode() throws IOException { if(curCodeBlockIndex >= 0){ if(curCodeBlock != null){ if(!curCodeBlock.hasPrev()){ try{ prevCodeBlock(); }catch(IOException e){ throw new RuntimeException(e); } if(curCodeBlock == null){ return false; }else{ return true; } }else{ return true; } }else{ try{ prevCodeBlock(); }catch(IOException e){ throw new RuntimeException(e); } if(curCodeBlock == null){ return false; }else{ return true; } } } return false; } private void nextCodeBlock() throws IOException{ ++curCodeBlockIndex; byte[] bytes = null; int count = 0; if(curCodeBlockIndex == maxCodeBlockIndex) { count = head.getCodeCount() - curCodeBlockIndex*DBConfig.BLOCK_MAX_COUNT; }else if(curCodeBlockIndex < maxCodeBlockIndex) { count = DBConfig.BLOCK_MAX_COUNT; }else{ curCodeBlock = null; return; } bytes = new byte[count*CodeItem.CODE_ITEM_SIZE]; storage.get(head.getCodeOffset() + curCodeBlockIndex*DBConfig.BLOCK_MAX_COUNT*CodeItem.CODE_ITEM_SIZE, bytes); curCodeBlock = new CodeBlock(bytes, count); curCodeItem = curCodeBlock.current(); } private void prevCodeBlock() throws IOException{ --curCodeBlockIndex; byte[] bytes = null; int count = 0; if(curCodeBlockIndex == maxCodeBlockIndex){ count = head.getCodeCount() - curCodeBlockIndex*DBConfig.BLOCK_MAX_COUNT; }else if(curCodeBlockIndex>=0){ count = DBConfig.BLOCK_MAX_COUNT; }else{ curCodeBlock = null; return; } bytes = new byte[count*CodeItem.CODE_ITEM_SIZE]; storage.get(head.getCodeOffset()+curCodeBlockIndex*DBConfig.BLOCK_MAX_COUNT*CodeItem.CODE_ITEM_SIZE, bytes); curCodeBlock = new CodeBlock(bytes, count); curCodeItem = curCodeBlock.last(); } private void nextTimeBlock() throws IOException{ ++curTimeBlockIndex; byte[] bytes = null; int count = 0; if(curTimeBlockIndex == maxTimeBlockIndex){ count = curCodeItem.getTimeCount() - curTimeBlockIndex*DBConfig.BLOCK_MAX_COUNT; }else if(curTimeBlockIndex < maxTimeBlockIndex){ count = DBConfig.BLOCK_MAX_COUNT; }else{ curTimeBlock = null; return; } bytes = new byte[count*TimeItem.TIME_ITEM_SIZE]; storage.get(curCodeItem.getTimeOffSet()+curTimeBlockIndex*DBConfig.BLOCK_MAX_COUNT*TimeItem.TIME_ITEM_SIZE, bytes); curTimeBlock = new TimeBlock(bytes, count); } private void prevTimeBlock() throws IOException{ --curTimeBlockIndex; byte[] bytes = null; int count = 0; if(curTimeBlockIndex == maxTimeBlockIndex){ count = curCodeItem.getTimeCount() - curTimeBlockIndex*DBConfig.BLOCK_MAX_COUNT; }else if(curTimeBlockIndex >= 0){ count = DBConfig.BLOCK_MAX_COUNT; }else{ curTimeBlock = null; return; } bytes = new byte[count*TimeItem.TIME_ITEM_SIZE]; storage.get(curCodeItem.getTimeOffSet()+curTimeBlockIndex*DBConfig.BLOCK_MAX_COUNT*TimeItem.TIME_ITEM_SIZE, bytes); curTimeBlock = new TimeBlock(bytes, count); } @Override public void seek(int code, long time) throws IOException { maxTimeBlockIndex = -2; curCodeBlockIndex = -1; curTimeBlockIndex = -1; curCodeItem = null; if (head.containCode(code)) { // read code area nextCodeBlock(); find(code); // read time area if (curCodeItem != null) { curTimeBlockIndex = -1; nextTimeBlock(); while (curTimeBlock.containTime(time) < 0) { nextTimeBlock(); if (curTimeBlock == null) { break; } } if (curTimeBlock != null) { if (curTimeBlock.containTime(time) == 0) { curTimeBlock.seek(time); } readEntry(code, curTimeBlock.current(), true); } else {// pointer to last time block last time item curTimeBlockIndex = maxTimeBlockIndex + 1; prevTimeBlock(); readEntry(code, curTimeBlock.last(), true); } return; } } curTimeBlockIndex = -1; maxTimeBlockIndex = -2; } @Override public void seekToFirst(int code,boolean isNext) throws IOException{ maxTimeBlockIndex = -2; curCodeBlockIndex = -1; curTimeBlockIndex = -1; curCodeItem = null; if (head.containCode(code)) { // read code area nextCodeBlock(); find(code); // read time area if (curCodeItem != null) { if(isNext){ nextTimeBlock(); if (curTimeBlock != null) { readEntry(curCodeItem.getCode(), curTimeBlock.current(), isNext); return; } }else{ curTimeBlockIndex = maxTimeBlockIndex + 1; prevTimeBlock(); if (curTimeBlock != null) { readEntry(curCodeItem.getCode(), curTimeBlock.last(), isNext); return; } } } } curTimeBlockIndex = -1; maxTimeBlockIndex = -2; } private void find(int code)throws IOException { if (curCodeBlock != null) { while(curCodeBlock.containCode(code)<0){ nextCodeBlock(); if (curCodeBlock == null) { break; } } if(curCodeBlock!=null){ if(!curCodeBlock.seek(code)){ curCodeBlock = null; } } } if (curCodeBlock != null) { curCodeItem = curCodeBlock.current(); if (curCodeItem != null) { maxTimeBlockIndex = (curCodeItem.getTimeCount() + DBConfig.BLOCK_MAX_COUNT-1)/ DBConfig.BLOCK_MAX_COUNT - 1; curTimeBlockIndex = -1; } } } @Override public boolean seekToCurrent(boolean isNext) throws IOException { // read code area if (curCodeBlock == null) { nextCodeBlock(); } if(curCodeBlock != null){ if(null == curCodeItem){ if(isNext){ curCodeItem = curCodeBlock.current(); }else{ curCodeItem = curCodeBlock.last(); } } } // read time area if (curCodeItem != null) { maxTimeBlockIndex = (curCodeItem.getTimeCount() + DBConfig.BLOCK_MAX_COUNT - 1)/ DBConfig.BLOCK_MAX_COUNT - 1; if(isNext){ curTimeBlockIndex = -1; nextTimeBlock(); }else{ curTimeBlockIndex = maxTimeBlockIndex + 1; prevTimeBlock(); } if (curTimeBlock != null) { if(isNext){ readEntry(curCodeItem.getCode(), curTimeBlock.current(), isNext); }else{ readEntry(curCodeItem.getCode(), curTimeBlock.last(), isNext); } return true; } } curTimeBlockIndex = -1; maxTimeBlockIndex = -2; return false; } @Override public InternalKey key() { if(curEntry != null){ return curEntry.getKey(); } return null; } @Override public long time() { if(curEntry != null){ return curEntry.getKey().getTime(); } return 0; } @Override public byte[] value() throws IOException { if(curEntry != null){ return curEntry.getValue(); } return null; } @Override public boolean valid() { if(curEntry != null){ return true; } return false; } private void readEntry(int code, TimeItem tItem, boolean isNext) throws IOException{ if(tItem == null){ if(isNext){ curTimeBlockIndex = maxTimeBlockIndex + 1; }else{ curTimeBlockIndex = -1; } curEntry = null; }else{ InternalKey key = new InternalKey(code, tItem.getTime()); byte[] value = new byte[tItem.getValueSize()]; storage.get(tItem.getValueOffset(), value); curEntry = new InternalEntry(key, value); } } @Override public Entry<InternalKey, byte[]> next() { if(curTimeBlock == null){ try{ nextTimeBlock(); }catch(IOException e){ throw new RuntimeException(e); } } if(curTimeBlock != null){ try{ readEntry(curCodeItem.getCode(),curTimeBlock.next(),true); }catch(IOException e){ throw new RuntimeException(e); } }else{ curEntry = null; } return curEntry; } @Override public Entry<InternalKey, byte[]> prev() { if(curTimeBlock == null){ try{ prevTimeBlock(); }catch(IOException e){ throw new RuntimeException(e); } } if(curTimeBlock != null){ try{ readEntry(curCodeItem.getCode(),curTimeBlock.prev(),false); }catch(IOException e){ throw new RuntimeException(e); } }else{ curEntry = null; } return curEntry; } @Override public CodeItem nextCode() throws IOException { if(curCodeBlock == null){ nextCodeBlock(); } if(curCodeBlock != null){ curCodeItem = curCodeBlock.next(); } if(curCodeItem == null){ nextCodeBlock(); if(curCodeBlock != null){ curCodeItem = curCodeBlock.next(); } } return curCodeItem; } @Override public CodeItem currentCode() throws IOException{ return curCodeItem; } @Override public CodeItem prevCode() throws IOException { if(curCodeBlock == null){ prevCodeBlock(); } if(curCodeBlock != null){ curCodeItem = curCodeBlock.prev(); } if(curCodeItem == null){ prevCodeBlock(); if(curCodeBlock != null){ curCodeItem = curCodeBlock.prev(); } } return curCodeItem; } @Override public void close() throws IOException { storage.close(); } @Override public long timeItemCount() { return head.getTimeCount(); } @Override public void remove() { throw new UnsupportedOperationException("unsupport remove operation!"); } @Override public long priority() { return fileNumber; } @Override public Entry<InternalKey, byte[]> current() { return curEntry; } @Override public boolean equals(Object obj) { if (!(obj instanceof FileSeekIterator)) { return false; } FileSeekIterator iterator = (FileSeekIterator) obj; return this.storage == iterator.storage; } }