/** * Remove all stored blocks for the given id. * * @param id the id */ public void remove(byte[] id) { ByteBuffer idBuffer = ByteBuffer.wrap(id); while (idBuffer.hasRemaining()) { switch (idBuffer.get()) { case 0: // in-place: 0, len (int), data int len = DataUtils.readVarInt(idBuffer); idBuffer.position(idBuffer.position() + len); break; case 1: // block: 1, len (int), blockId (long) DataUtils.readVarInt(idBuffer); long k = DataUtils.readVarLong(idBuffer); map.remove(k); break; case 2: // indirect: 2, total len (long), blockId (long) DataUtils.readVarLong(idBuffer); long k2 = DataUtils.readVarLong(idBuffer); // recurse remove(map.get(k2)); map.remove(k2); break; default: throw DataUtils.newIllegalArgumentException("Unsupported id {0}", Arrays.toString(id)); } } }
/** * Calculate the number of data bytes for the given id. As the length is encoded in the id, this * operation does not cause any reads in the map. * * @param id the id * @return the length */ public long length(byte[] id) { ByteBuffer idBuffer = ByteBuffer.wrap(id); long length = 0; while (idBuffer.hasRemaining()) { switch (idBuffer.get()) { case 0: // in-place: 0, len (int), data int len = DataUtils.readVarInt(idBuffer); idBuffer.position(idBuffer.position() + len); length += len; break; case 1: // block: 1, len (int), blockId (long) length += DataUtils.readVarInt(idBuffer); DataUtils.readVarLong(idBuffer); break; case 2: // indirect: 2, total len (long), blockId (long) length += DataUtils.readVarLong(idBuffer); DataUtils.readVarLong(idBuffer); break; default: throw DataUtils.newIllegalArgumentException("Unsupported id {0}", Arrays.toString(id)); } } return length; }
/** * Get the key of the biggest block, of -1 for inline data. This method is used to garbage collect * orphaned blocks. * * @param id the id * @return the key, or -1 */ public long getMaxBlockKey(byte[] id) { long maxKey = -1; ByteBuffer idBuffer = ByteBuffer.wrap(id); while (idBuffer.hasRemaining()) { switch (idBuffer.get()) { case 0: // in-place: 0, len (int), data int len = DataUtils.readVarInt(idBuffer); idBuffer.position(idBuffer.position() + len); break; case 1: // block: 1, len (int), blockId (long) DataUtils.readVarInt(idBuffer); long k = DataUtils.readVarLong(idBuffer); maxKey = Math.max(maxKey, k); break; case 2: // indirect: 2, total len (long), blockId (long) DataUtils.readVarLong(idBuffer); long k2 = DataUtils.readVarLong(idBuffer); // recurse byte[] r = map.get(k2); maxKey = Math.max(maxKey, getMaxBlockKey(r)); break; default: throw DataUtils.newIllegalArgumentException("Unsupported id {0}", Arrays.toString(id)); } } return maxKey; }
private ByteArrayInputStream nextBuffer() { while (idBuffer.hasRemaining()) { switch (idBuffer.get()) { case 0: { int len = DataUtils.readVarInt(idBuffer); if (skip >= len) { skip -= len; idBuffer.position(idBuffer.position() + len); continue; } int p = (int) (idBuffer.position() + skip); int l = (int) (len - skip); idBuffer.position(p + l); return new ByteArrayInputStream(idBuffer.array(), p, l); } case 1: { int len = DataUtils.readVarInt(idBuffer); long key = DataUtils.readVarLong(idBuffer); if (skip >= len) { skip -= len; continue; } byte[] data = store.getBlock(key); int s = (int) skip; skip = 0; return new ByteArrayInputStream(data, s, data.length - s); } case 2: { long len = DataUtils.readVarLong(idBuffer); long key = DataUtils.readVarLong(idBuffer); if (skip >= len) { skip -= len; continue; } byte[] k = store.getBlock(key); ByteBuffer newBuffer = ByteBuffer.allocate(k.length + idBuffer.limit() - idBuffer.position()); newBuffer.put(k); newBuffer.put(idBuffer); newBuffer.flip(); idBuffer = newBuffer; return nextBuffer(); } default: throw DataUtils.newIllegalArgumentException( "Unsupported id {0}", Arrays.toString(idBuffer.array())); } } return null; }
/** * Try to open the file. * * @param fileName the file name * @param readOnly whether the file should only be opened in read-only mode, even if the file is * writable * @param encryptionKey the encryption key, or null if encryption is not used */ public void open(String fileName, boolean readOnly, char[] encryptionKey) { if (file != null) { return; } if (fileName != null) { FilePath p = FilePath.get(fileName); // if no explicit scheme was specified, NIO is used if (p instanceof FilePathDisk && !fileName.startsWith(p.getScheme() + ":")) { // ensure the NIO file system is registered FilePathNio.class.getName(); fileName = "nio:" + fileName; } } this.fileName = fileName; FilePath f = FilePath.get(fileName); FilePath parent = f.getParent(); if (parent != null && !parent.exists()) { throw DataUtils.newIllegalArgumentException("Directory does not exist: {0}", parent); } if (f.exists() && !f.canWrite()) { readOnly = true; } this.readOnly = readOnly; try { file = f.open(readOnly ? "r" : "rw"); if (encryptionKey != null) { byte[] key = FilePathEncrypt.getPasswordBytes(encryptionKey); encryptedFile = file; file = new FilePathEncrypt.FileEncrypt(fileName, key, file); } file = FilePathCache.wrap(file); try { if (readOnly) { fileLock = file.tryLock(0, Long.MAX_VALUE, true); } else { fileLock = file.tryLock(); } } catch (OverlappingFileLockException e) { throw DataUtils.newIllegalStateException( DataUtils.ERROR_FILE_LOCKED, "The file is locked: {0}", fileName, e); } if (fileLock == null) { throw DataUtils.newIllegalStateException( DataUtils.ERROR_FILE_LOCKED, "The file is locked: {0}", fileName); } fileSize = file.size(); } catch (IOException e) { throw DataUtils.newIllegalStateException( DataUtils.ERROR_READING_FAILED, "Could not open file {0}", fileName, e); } }