1 23 package org.archive.util.ms; 24 25 import java.io.IOException ; 26 import java.util.logging.Level ; 27 import java.util.logging.Logger ; 28 29 import org.archive.io.BufferedSeekInputStream; 30 import org.archive.io.Endian; 31 import org.archive.io.OriginSeekInputStream; 32 import org.archive.io.SafeSeekInputStream; 33 import org.archive.io.SeekInputStream; 34 35 36 75 class PieceTable { 76 77 final static Logger LOGGER 78 = Logger.getLogger(PieceTable.class.getName()); 79 80 81 final static int CP1252_INDICATOR = 1 << 30; 82 83 84 final static int CP1252_MASK = ~(3 << 30); 85 86 87 private int count; 88 89 90 private int maxCharPos; 91 92 93 private int current; 94 95 96 private Piece currentPiece; 97 98 99 100 private SeekInputStream charPos; 101 102 103 private SeekInputStream filePos; 104 105 106 115 public PieceTable(SeekInputStream tableStream, int offset, 116 int maxCharPos, int cachedRecords) throws IOException { 117 tableStream.position(offset); 118 skipProperties(tableStream); 119 int sizeInBytes = Endian.littleInt(tableStream); 120 this.count = (sizeInBytes - 4) / 12; 121 cachedRecords = Math.min(cachedRecords, count); 122 long tp = tableStream.position() + 4; 123 long charPosStart = tp; 124 long filePosStart = tp + count * 4 + 2; 125 126 this.filePos = wrap(tableStream, filePosStart, cachedRecords * 8); 127 this.charPos = wrap(tableStream, charPosStart, cachedRecords * 4); 128 this.maxCharPos = maxCharPos; 129 130 if (LOGGER.isLoggable(Level.FINEST)) { 131 LOGGER.finest("Size in bytes: " + sizeInBytes); 132 LOGGER.finest("Piece table count: " + count); 133 for (Piece piece = next(); piece != null; piece = next()) { 134 LOGGER.finest("#" + current + ": " + piece.toString()); 135 } 136 current = 0; 137 } 138 } 139 140 141 156 private SeekInputStream wrap(SeekInputStream input, long pos, int cache) 157 throws IOException { 158 input.position(pos); 159 SeekInputStream r = new SafeSeekInputStream(input); 160 r = new OriginSeekInputStream(r, pos); 161 r = new BufferedSeekInputStream(r, cache); 162 return r; 163 } 164 165 166 177 private static void skipProperties(SeekInputStream input) throws IOException { 178 int tag = input.read(); 179 while (tag == 1) { 180 int size = Endian.littleChar(input); 181 while (size > 0) { 182 size -= input.skip(size); 183 } 184 tag = input.read(); 185 } 186 if (tag != 2) { 187 throw new IllegalStateException (); 188 } 189 } 190 191 192 198 public int getMaxCharPos() { 199 return maxCharPos; 200 } 201 202 203 210 public Piece next() throws IOException { 211 if (current >= count) { 212 currentPiece = null; 213 return null; 214 } 215 216 int cp; 217 if (current == count - 1) { 218 cp = maxCharPos; 219 } else { 220 charPos.position(current * 4); 221 cp = Endian.littleInt(charPos); 222 } 223 filePos.position(current * 8); 224 int encoded = Endian.littleInt(filePos); 225 226 if (LOGGER.isLoggable(Level.FINEST)) { 227 StringBuffer sb = new StringBuffer (Integer.toBinaryString(encoded)); 228 while (sb.length() < 32) { 229 sb.insert(0, '0'); 230 } 231 LOGGER.finest("Encoded offset: " + sb.toString()); 232 } 233 234 current++; 235 236 int start; 237 if (currentPiece == null) { 238 start = 0; 239 } else { 240 start = currentPiece.getCharPosLimit(); 241 } 242 if ((encoded & CP1252_INDICATOR) == 0) { 243 Piece piece = new Piece(encoded, start, cp, true); 244 currentPiece = piece; 245 return piece; 246 } else { 247 int filePos = (encoded & CP1252_MASK) / 2; 248 Piece piece = new Piece(filePos, start, cp, false); 249 currentPiece = piece; 250 return piece; 251 } 252 } 253 254 255 263 public Piece pieceFor(int charPos) throws IOException { 264 if (currentPiece.contains(charPos)) { 265 return currentPiece; 266 } 267 268 270 current = 0; 271 currentPiece = null; 272 next(); 273 274 while (currentPiece != null) { 275 if (currentPiece.contains(charPos)) { 276 return currentPiece; 277 } 278 next(); 279 } 280 281 return null; 282 } 283 284 } 285 | Popular Tags |