private long readHeader(int id) throws IOException { long header = p_alloc(MINIMUM_PAGE_SIZE); try { try { mPageArray.readPage(id, header, 0, MINIMUM_PAGE_SIZE); } catch (EOFException e) { throw new CorruptDatabaseException("File is smaller than expected"); } long magic = p_longGetLE(header, I_MAGIC_NUMBER); if (magic != MAGIC_NUMBER) { throw new CorruptDatabaseException("Wrong magic number: " + magic); } int checksum = p_intGetLE(header, I_CHECKSUM); int newChecksum = setHeaderChecksum(header); if (newChecksum != checksum) { throw new CorruptDatabaseException( "Header checksum mismatch: " + newChecksum + " != " + checksum); } return header; } catch (Throwable e) { p_delete(header); throw e; } }
/** @see _SnapshotPageArray#beginSnapshot */ Snapshot beginSnapshot(_LocalDatabase db) throws IOException { mHeaderLatch.acquireShared(); try { long pageCount, redoPos; long header = p_alloc(MINIMUM_PAGE_SIZE); try { mPageArray.readPage(mCommitNumber & 1, header, 0, MINIMUM_PAGE_SIZE); pageCount = _PageManager.readTotalPageCount(header, I_MANAGER_HEADER); redoPos = _LocalDatabase.readRedoPosition(header, I_EXTRA_DATA); } finally { p_delete(header); } return mPageArray.beginSnapshot(db, pageCount, redoPos); } finally { mHeaderLatch.releaseShared(); } }
@Override public void readPage(long id, long page) throws IOException { try { mPageArray.readPage(id, page, 0, pageSize()); } catch (Throwable e) { throw closeOnFailure(e); } }
private void readPartial(long index, int start, byte[] buf, int offset, int length) throws IOException { long page = p_alloc(start + length); try { mPageArray.readPage(index, page, 0, start + length); p_copyToArray(page, start, buf, offset, length); } finally { p_delete(page); } }
@Override public void deletePage(long id) throws IOException { checkId(id); CommitLock.Shared shared = mCommitLock.acquireShared(); try { mPageManager.deletePage(id); } catch (IOException e) { throw e; } catch (Throwable e) { throw closeOnFailure(e); } finally { shared.release(); } mPageArray.uncachePage(id); }
@Override public void close(Throwable cause) throws IOException { if (mPageArray != null) { mPageArray.close(cause); } }
@Override public long copyPage(long srcId, long dstId) throws IOException { return mPageArray.copyPage(srcId, dstId); }
@Override public long dirtyPage(long id) throws IOException { return mPageArray.dirtyPage(id); }
@Override public long directPagePointer(long id) throws IOException { return mPageArray.directPagePointer(id); }
@Override public void uncachePage(long id) throws IOException { mPageArray.uncachePage(id); }
@Override public void cachePage(long id, long page) throws IOException { mPageArray.cachePage(id, page); }
@Override public long evictPage(long id, long page) throws IOException { checkId(id); return mPageArray.evictPage(id, page); }
@Override public void writePage(long id, long page) throws IOException { checkId(id); mPageArray.writePage(id, page, 0); }
@Override public long pageCount() throws IOException { return mPageArray.getPageCount(); }
@Override public int pageSize() { return mPageArray.pageSize(); }
private _DurablePageDb( final PageArray rawArray, final PageCache cache, final Crypto crypto, final boolean destroy) throws IOException, WrongPageSize { mCrypto = crypto; PageArray array = crypto == null ? rawArray : new CryptoPageArray(rawArray, crypto); mPageArray = new _SnapshotPageArray(array, rawArray, cache); mHeaderLatch = new Latch(); try { int pageSize = mPageArray.pageSize(); checkPageSize(pageSize); if (destroy || mPageArray.isEmpty()) { // Newly created file. mPageManager = new _PageManager(mPageArray); mCommitNumber = -1; // Commit twice to ensure both headers have valid data. long header = p_calloc(pageSize); try { mCommitLock.acquireExclusive(); try { commit(false, header, null); commit(false, header, null); } finally { mCommitLock.releaseExclusive(); } } finally { p_delete(header); } mPageArray.setPageCount(2); } else { // Opened an existing file. // Previous header commit operation might have been interrupted before final // header sync completed. Pages cannot be safely recycled without this. mPageArray.sync(false); long header0 = p_null(); long header1 = p_null(); try { final long header; final int commitNumber; findHeader: { int pageSize0; int commitNumber0, commitNumber1; CorruptDatabaseException ex0; try { header0 = readHeader(0); commitNumber0 = p_intGetLE(header0, I_COMMIT_NUMBER); pageSize0 = p_intGetLE(header0, I_PAGE_SIZE); ex0 = null; } catch (CorruptDatabaseException e) { header0 = p_null(); commitNumber0 = -1; pageSize0 = pageSize; ex0 = e; } if (pageSize0 != pageSize) { throw new WrongPageSize(pageSize, pageSize0); } try { header1 = readHeader(1); commitNumber1 = p_intGetLE(header1, I_COMMIT_NUMBER); } catch (CorruptDatabaseException e) { if (ex0 != null) { // File is completely unusable. throw ex0; } header = header0; commitNumber = commitNumber0; break findHeader; } int pageSize1 = p_intGetLE(header1, I_PAGE_SIZE); if (pageSize0 != pageSize1) { throw new CorruptDatabaseException( "Mismatched page sizes: " + pageSize0 + " != " + pageSize1); } if (header0 == p_null()) { header = header1; commitNumber = commitNumber1; } else { // Modulo comparison. int diff = commitNumber1 - commitNumber0; if (diff > 0) { header = header1; commitNumber = commitNumber1; } else if (diff < 0) { header = header0; commitNumber = commitNumber0; } else { throw new CorruptDatabaseException( "Both headers have same commit number: " + commitNumber0); } } } mHeaderLatch.acquireExclusive(); mCommitNumber = commitNumber; mHeaderLatch.releaseExclusive(); mPageManager = new _PageManager(mPageArray, header, I_MANAGER_HEADER); } finally { p_delete(header0); p_delete(header1); } } } catch (WrongPageSize e) { delete(); closeQuietly(null, this); throw e; } catch (Throwable e) { delete(); throw closeOnFailure(e); } }