1 23 package org.archive.util.ms; 24 25 import java.io.IOException ; 26 import java.nio.ByteBuffer ; 27 import java.nio.ByteOrder ; 28 import java.util.Map ; 29 30 import org.archive.io.SeekInputStream; 31 import org.archive.util.IoUtils; 32 import org.archive.util.LRU; 33 34 35 100 public class DefaultBlockFileSystem implements BlockFileSystem { 101 102 103 106 final private static int POINTERS_PER_BAT = 128; 107 108 109 112 final private static int BAT_POINTER_SIZE = BLOCK_SIZE / POINTERS_PER_BAT; 113 114 115 119 final private static int HEADER_BAT_LIMIT = 109; 120 121 122 125 final private static int ENTRY_SIZE = 128; 126 127 128 131 final private static int ENTRIES_PER_BLOCK = BLOCK_SIZE / ENTRY_SIZE; 132 133 134 137 private SeekInputStream input; 138 139 140 143 private HeaderBlock header; 144 145 146 149 private Map <Integer ,ByteBuffer > cache; 150 151 152 159 public DefaultBlockFileSystem(SeekInputStream input, int batCacheSize) 160 throws IOException { 161 this.input = input; 162 byte[] temp = new byte[BLOCK_SIZE]; 163 IoUtils.readFully(input, temp); 164 this.header = new HeaderBlock(ByteBuffer.wrap(temp)); 165 this.cache = new LRU<Integer ,ByteBuffer >(batCacheSize); 166 } 167 168 169 public Entry getRoot() throws IOException { 170 int block = header.getEntriesStart(); 172 input.position((block + 1) * BLOCK_SIZE); 173 174 return new DefaultEntry(this, input, 0); 176 } 177 178 179 186 Entry getEntry(int entryNumber) throws IOException { 187 if (entryNumber < 0) { 189 return null; 190 } 191 192 195 int blockCount = entryNumber / ENTRIES_PER_BLOCK; 197 int remainder = entryNumber % ENTRIES_PER_BLOCK; 198 int block = header.getEntriesStart(); 199 for (int i = 0; i < blockCount; i++) { 200 block = getNextBlock(block); 201 } 202 203 if (block < 0) { 204 return null; 206 } 207 208 int filePos = (block + 1) * BLOCK_SIZE + remainder * ENTRY_SIZE; 209 input.position(filePos); 210 211 return new DefaultEntry(this, input, entryNumber); 212 } 213 214 215 public int getNextBlock(int block) throws IOException { 216 if (block < 0) { 217 return block; 218 } 219 220 int headerIndex = block / POINTERS_PER_BAT; 222 223 int batBlockIndex = block % POINTERS_PER_BAT; 225 226 int batBlockNumber = batLookup(headerIndex); 227 ByteBuffer batBlock = getBATBlock(batBlockNumber); 228 return batBlock.getInt(batBlockIndex * BAT_POINTER_SIZE); 229 } 230 231 232 239 private int batLookup(int headerIndex) throws IOException { 240 if (headerIndex < HEADER_BAT_LIMIT + 1) { 241 return header.getBATBlockNumber(headerIndex); 242 } 243 244 headerIndex -= HEADER_BAT_LIMIT; 246 int xbatBlockNumber = headerIndex / POINTERS_PER_BAT; 247 xbatBlockNumber += header.getExtendedBATStart(); 248 ByteBuffer xbat = getBATBlock(xbatBlockNumber); 249 250 int xbatBlockIndex = headerIndex % POINTERS_PER_BAT; 252 return xbat.getInt(xbatBlockIndex * BAT_POINTER_SIZE); 253 } 254 255 256 267 private ByteBuffer getBATBlock(int block) throws IOException { 268 ByteBuffer r = cache.get(block); 269 if (r != null) { 270 return r; 271 } 272 273 byte[] buf = new byte[BLOCK_SIZE]; 274 input.position((block + 1) * BLOCK_SIZE); 275 IoUtils.readFully(input, buf); 276 277 r = ByteBuffer.wrap(buf); 278 r.order(ByteOrder.LITTLE_ENDIAN); 279 cache.put(block, r); 280 return r; 281 } 282 283 284 public SeekInputStream getRawInput() { 285 return input; 286 } 287 } 288 | Popular Tags |