Пример #1
0
  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);
    }
  }