AbstractBlobStore implements GarbageCollectableBlobStore,
        Cache.Backend<AbstractBlobStore.BlockId, AbstractBlobStore.Data> { @Override public int readBlob(String blobId, long pos, byte[] buff, int off, int length) throws IOException { if (isMarkEnabled()) { mark(blobId); } byte[] id = StringUtils.convertHexToBytes(blobId); ByteArrayInputStream idStream = new ByteArrayInputStream(id); while (true) { int type = idStream.read(); if (type == -1) { statsCollector.downloadCompleted(blobId); return -1; } else if (type == TYPE_DATA) { int len = IOUtils.readVarInt(idStream); if (pos < len) { IOUtils.skipFully(idStream, (int) pos); len -= pos; if (length < len) { len = length; } IOUtils.readFully(idStream, buff, off, len); return len; } IOUtils.skipFully(idStream, len); pos -= len; } else if (type == TYPE_HASH) { int level = IOUtils.readVarInt(idStream); long totalLength = IOUtils.readVarLong(idStream); if (level > 0) { IOUtils.readVarLong(idStream); } byte[] digest = new byte[IOUtils.readVarInt(idStream)]; IOUtils.readFully(idStream, digest, 0, digest.length); if (pos >= totalLength) { pos -= totalLength; } else { if (level > 0) { byte[] block = readBlock(digest, 0); idStream = new ByteArrayInputStream(block); } else { long readPos = pos - pos % blockSize; byte[] block = readBlock(digest, readPos); ByteArrayInputStream in = new ByteArrayInputStream(block); IOUtils.skipFully(in, pos - readPos); return IOUtils.readFully(in, buff, off, length); } } } else { throw new IOException("Unknown blobs id type " + type + " for blob " + blobId); } } }  void setBlockSizeMin(int x); @Override long getBlockSizeMin(); @Override void setBlockSize(int x); void setStatsCollector(BlobStatsCollector stats); int getBlockSize(); @Override String writeBlob(String tempFilePath); @Override String writeBlob(InputStream in); @Override String writeBlob(InputStream in, BlobOptions options); @Override InputStream getInputStream(String blobId); @Override String getReference(@NotNull String blobId); @Override String getBlobId(@NotNull String reference); void setReferenceKey(byte[] referenceKey); void setReferenceKeyEncoded(String encodedKey); void setReferenceKeyPlainText(String textKey); @Override void clearInUse(); @Override abstract void startMark(); @Override abstract int sweep(); @Override int readBlob(String blobId, long pos, byte[] buff, int off,
            int length); @Override Data load(BlockId id); @Override long getBlobLength(String blobId); @Override Iterator<String> resolveChunks(String blobId); @Override boolean deleteChunks(List<String> chunkIds, long maxLastModifiedTime); @Override void close();  }
@Test public void testIllegalIdentifier() throws Exception { byte[] data = new byte[1]; try { store.readBlob("ff", 0, data, 0, 1); fail(); } catch (Exception e) { } }