1721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor/*
2721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor * Copyright (C) 2012 The Android Open Source Project
3721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor *
4721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor * Licensed under the Apache License, Version 2.0 (the "License");
5721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor * you may not use this file except in compliance with the License.
6721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor * You may obtain a copy of the License at
7721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor *
8721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor *      http://www.apache.org/licenses/LICENSE-2.0
9721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor *
10721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor * Unless required by applicable law or agreed to in writing, software
11721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor * distributed under the License is distributed on an "AS IS" BASIS,
12721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor * See the License for the specific language governing permissions and
14721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor * limitations under the License.
15721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor */
16721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
17721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// This is an on-disk cache which maps a 64-bits key to a byte array.
18721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor//
19721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// It consists of three files: one index file and two data files. One of the
20721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// data files is "active", and the other is "inactive". New entries are
21721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// appended into the active region until it reaches the size limit. At that
22721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// point the active file and the inactive file are swapped, and the new active
23721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// file is truncated to empty (and the index for that file is also cleared).
24721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// The index is a hash table with linear probing. When the load factor reaches
25721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// 0.5, it does the same thing like when the size limit is reached.
26721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor//
27721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// The index file format: (all numbers are stored in little-endian)
28721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// [0]  Magic number: 0xB3273030
29721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// [4]  MaxEntries: Max number of hash entries per region.
30721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// [8]  MaxBytes: Max number of data bytes per region (including header).
31721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// [12] ActiveRegion: The active growing region: 0 or 1.
32721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// [16] ActiveEntries: The number of hash entries used in the active region.
33721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// [20] ActiveBytes: The number of data bytes used in the active region.
34721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// [24] Version number.
35721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// [28] Checksum of [0..28).
36721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// [32] Hash entries for region 0. The size is X = (12 * MaxEntries bytes).
37721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// [32 + X] Hash entries for region 1. The size is also X.
38721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor//
39721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// Each hash entry is 12 bytes: 8 bytes key and 4 bytes offset into the data
40721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// file. The offset is 0 when the slot is free. Note that 0 is a valid value
41721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// for key. The keys are used directly as index into a hash table, so they
42721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// should be suitably distributed.
43721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor//
44721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// Each data file stores data for one region. The data file is concatenated
45721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// blobs followed by the magic number 0xBD248510.
46721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor//
47721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// The blob format:
48721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// [0]  Key of this blob
49721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// [8]  Checksum of this blob
50721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// [12] Offset of this blob
51721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// [16] Length of this blob (not including header)
52721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// [20] Blob
53721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor//
54721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// Below are the interface for BlobCache. The instance of this class does not
55721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// support concurrent use by multiple threads.
56721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor//
57721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// public BlobCache(String path, int maxEntries, int maxBytes, boolean reset) throws IOException;
58721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// public void insert(long key, byte[] data) throws IOException;
59721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// public byte[] lookup(long key) throws IOException;
60721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// public void lookup(LookupRequest req) throws IOException;
61721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// public void close();
62721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// public void syncIndex();
63721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// public void syncAll();
64721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor// public static void deleteFiles(String path);
65721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor//
66721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylorpackage com.android.mms.util;
67721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
68ab845dee6565a8dfc384186bc8f2e801a2b087e1Ye Wenimport com.android.mms.LogTag;
69ab845dee6565a8dfc384186bc8f2e801a2b087e1Ye Wen
70721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylorimport java.io.Closeable;
71721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylorimport java.io.File;
72721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylorimport java.io.IOException;
73721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylorimport java.io.RandomAccessFile;
74721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylorimport java.nio.ByteOrder;
75721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylorimport java.nio.MappedByteBuffer;
76721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylorimport java.nio.channels.FileChannel;
77721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylorimport java.util.zip.Adler32;
78721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
79d64419030e1fec1e751695dab3bd7236e2fb0214Roger Chenimport android.util.Log;
80d64419030e1fec1e751695dab3bd7236e2fb0214Roger Chen
81721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylorpublic class BlobCache implements Closeable {
82ab845dee6565a8dfc384186bc8f2e801a2b087e1Ye Wen    private static final String TAG = LogTag.TAG;
83721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
84721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private static final int MAGIC_INDEX_FILE = 0xB3273030;
85721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private static final int MAGIC_DATA_FILE = 0xBD248510;
86721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
87721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // index header offset
88721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private static final int IH_MAGIC = 0;
89721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private static final int IH_MAX_ENTRIES = 4;
90721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private static final int IH_MAX_BYTES = 8;
91721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private static final int IH_ACTIVE_REGION = 12;
92721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private static final int IH_ACTIVE_ENTRIES = 16;
93721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private static final int IH_ACTIVE_BYTES = 20;
94721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private static final int IH_VERSION = 24;
95721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private static final int IH_CHECKSUM = 28;
96721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private static final int INDEX_HEADER_SIZE = 32;
97721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
98721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private static final int DATA_HEADER_SIZE = 4;
99721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
100721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // blob header offset
101721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private static final int BH_KEY = 0;
102721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private static final int BH_CHECKSUM = 8;
103721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private static final int BH_OFFSET = 12;
104721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private static final int BH_LENGTH = 16;
105721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private static final int BLOB_HEADER_SIZE = 20;
106721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
107721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private RandomAccessFile mIndexFile;
108721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private RandomAccessFile mDataFile0;
109721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private RandomAccessFile mDataFile1;
110721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private FileChannel mIndexChannel;
111721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private MappedByteBuffer mIndexBuffer;
112721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
113721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private int mMaxEntries;
114721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private int mMaxBytes;
115721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private int mActiveRegion;
116721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private int mActiveEntries;
117721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private int mActiveBytes;
118721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private int mVersion;
119721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
120721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private RandomAccessFile mActiveDataFile;
121721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private RandomAccessFile mInactiveDataFile;
122721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private int mActiveHashStart;
123721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private int mInactiveHashStart;
124721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private byte[] mIndexHeader = new byte[INDEX_HEADER_SIZE];
125721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private byte[] mBlobHeader = new byte[BLOB_HEADER_SIZE];
126721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private Adler32 mAdler32 = new Adler32();
127721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
128721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // Creates the cache. Three files will be created:
129721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // path + ".idx", path + ".0", and path + ".1"
130721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // The ".0" file and the ".1" file each stores data for a region. Each of
131721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // them can grow to the size specified by maxBytes. The maxEntries parameter
132721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // specifies the maximum number of entries each region can have. If the
133721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // "reset" parameter is true, the cache will be cleared before use.
134721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    public BlobCache(String path, int maxEntries, int maxBytes, boolean reset)
135721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            throws IOException {
136721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        this(path, maxEntries, maxBytes, reset, 0);
137721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    }
138721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
139721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    public BlobCache(String path, int maxEntries, int maxBytes, boolean reset,
140721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            int version) throws IOException {
141721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        mIndexFile = new RandomAccessFile(path + ".idx", "rw");
142721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        mDataFile0 = new RandomAccessFile(path + ".0", "rw");
143721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        mDataFile1 = new RandomAccessFile(path + ".1", "rw");
144721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        mVersion = version;
145721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
146721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        if (!reset && loadIndex()) {
147721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            return;
148721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        }
149721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
150721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        resetCache(maxEntries, maxBytes);
151721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
152721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        if (!loadIndex()) {
153721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            closeAll();
154721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            throw new IOException("unable to load index");
155721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        }
156721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    }
157721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
158721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // Delete the files associated with the given path previously created
159721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // by the BlobCache constructor.
160721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    public static void deleteFiles(String path) {
161721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        deleteFileSilently(path + ".idx");
162721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        deleteFileSilently(path + ".0");
163721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        deleteFileSilently(path + ".1");
164721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    }
165721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
166721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private static void deleteFileSilently(String path) {
167721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        try {
168721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            new File(path).delete();
169721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        } catch (Throwable t) {
170721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            // ignore;
171721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        }
172721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    }
173721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
174721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // Close the cache. All resources are released. No other method should be
175721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // called after this is called.
176721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    @Override
177721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    public void close() {
178721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        syncAll();
179721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        closeAll();
180721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    }
181721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
182721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private void closeAll() {
183721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        closeSilently(mIndexChannel);
184721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        closeSilently(mIndexFile);
185721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        closeSilently(mDataFile0);
186721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        closeSilently(mDataFile1);
187721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    }
188721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
189721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // Returns true if loading index is successful. After this method is called,
190721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // mIndexHeader and index header in file should be kept sync.
191721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private boolean loadIndex() {
192721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        try {
193721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            mIndexFile.seek(0);
194721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            mDataFile0.seek(0);
195721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            mDataFile1.seek(0);
196721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
197721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            byte[] buf = mIndexHeader;
198721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            if (mIndexFile.read(buf) != INDEX_HEADER_SIZE) {
199721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                Log.w(TAG, "cannot read header");
200721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                return false;
201721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            }
202721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
203721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            if (readInt(buf, IH_MAGIC) != MAGIC_INDEX_FILE) {
204721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                Log.w(TAG, "cannot read header magic");
205721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                return false;
206721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            }
207721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
208721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            if (readInt(buf, IH_VERSION) != mVersion) {
209721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                Log.w(TAG, "version mismatch");
210721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                return false;
211721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            }
212721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
213721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            mMaxEntries = readInt(buf, IH_MAX_ENTRIES);
214721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            mMaxBytes = readInt(buf, IH_MAX_BYTES);
215721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            mActiveRegion = readInt(buf, IH_ACTIVE_REGION);
216721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            mActiveEntries = readInt(buf, IH_ACTIVE_ENTRIES);
217721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            mActiveBytes = readInt(buf, IH_ACTIVE_BYTES);
218721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
219721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            int sum = readInt(buf, IH_CHECKSUM);
220721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            if (checkSum(buf, 0, IH_CHECKSUM) != sum) {
221721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                Log.w(TAG, "header checksum does not match");
222721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                return false;
223721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            }
224721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
225721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            // Sanity check
226721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            if (mMaxEntries <= 0) {
227721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                Log.w(TAG, "invalid max entries");
228721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                return false;
229721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            }
230721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            if (mMaxBytes <= 0) {
231721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                Log.w(TAG, "invalid max bytes");
232721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                return false;
233721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            }
234721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            if (mActiveRegion != 0 && mActiveRegion != 1) {
235721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                Log.w(TAG, "invalid active region");
236721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                return false;
237721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            }
238721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            if (mActiveEntries < 0 || mActiveEntries > mMaxEntries) {
239721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                Log.w(TAG, "invalid active entries");
240721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                return false;
241721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            }
242721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            if (mActiveBytes < DATA_HEADER_SIZE || mActiveBytes > mMaxBytes) {
243721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                Log.w(TAG, "invalid active bytes");
244721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                return false;
245721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            }
246721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            if (mIndexFile.length() !=
247721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                    INDEX_HEADER_SIZE + mMaxEntries * 12 * 2) {
248721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                Log.w(TAG, "invalid index file length");
249721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                return false;
250721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            }
251721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
252721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            // Make sure data file has magic
253721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            byte[] magic = new byte[4];
254721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            if (mDataFile0.read(magic) != 4) {
255721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                Log.w(TAG, "cannot read data file magic");
256721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                return false;
257721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            }
258721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            if (readInt(magic, 0) != MAGIC_DATA_FILE) {
259721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                Log.w(TAG, "invalid data file magic");
260721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                return false;
261721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            }
262721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            if (mDataFile1.read(magic) != 4) {
263721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                Log.w(TAG, "cannot read data file magic");
264721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                return false;
265721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            }
266721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            if (readInt(magic, 0) != MAGIC_DATA_FILE) {
267721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                Log.w(TAG, "invalid data file magic");
268721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                return false;
269721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            }
270721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
271721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            // Map index file to memory
272721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            mIndexChannel = mIndexFile.getChannel();
273721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            mIndexBuffer = mIndexChannel.map(FileChannel.MapMode.READ_WRITE,
274721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                    0, mIndexFile.length());
275721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            mIndexBuffer.order(ByteOrder.LITTLE_ENDIAN);
276721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
277721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            setActiveVariables();
278721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            return true;
279721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        } catch (IOException ex) {
280721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            Log.e(TAG, "loadIndex failed.", ex);
281721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            return false;
282721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        }
283721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    }
284721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
285721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private void setActiveVariables() throws IOException {
286721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        mActiveDataFile = (mActiveRegion == 0) ? mDataFile0 : mDataFile1;
287721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        mInactiveDataFile = (mActiveRegion == 1) ? mDataFile0 : mDataFile1;
288721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        mActiveDataFile.setLength(mActiveBytes);
289721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        mActiveDataFile.seek(mActiveBytes);
290721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
291721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        mActiveHashStart = INDEX_HEADER_SIZE;
292721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        mInactiveHashStart = INDEX_HEADER_SIZE;
293721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
294721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        if (mActiveRegion == 0) {
295721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            mInactiveHashStart += mMaxEntries * 12;
296721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        } else {
297721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            mActiveHashStart += mMaxEntries * 12;
298721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        }
299721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    }
300721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
301721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private void resetCache(int maxEntries, int maxBytes) throws IOException {
302721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        mIndexFile.setLength(0);  // truncate to zero the index
303721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        mIndexFile.setLength(INDEX_HEADER_SIZE + maxEntries * 12 * 2);
304721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        mIndexFile.seek(0);
305721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        byte[] buf = mIndexHeader;
306721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        writeInt(buf, IH_MAGIC, MAGIC_INDEX_FILE);
307721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        writeInt(buf, IH_MAX_ENTRIES, maxEntries);
308721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        writeInt(buf, IH_MAX_BYTES, maxBytes);
309721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        writeInt(buf, IH_ACTIVE_REGION, 0);
310721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        writeInt(buf, IH_ACTIVE_ENTRIES, 0);
311721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        writeInt(buf, IH_ACTIVE_BYTES, DATA_HEADER_SIZE);
312721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        writeInt(buf, IH_VERSION, mVersion);
313721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        writeInt(buf, IH_CHECKSUM, checkSum(buf, 0, IH_CHECKSUM));
314721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        mIndexFile.write(buf);
315721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        // This is only needed if setLength does not zero the extended part.
316721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        // writeZero(mIndexFile, maxEntries * 12 * 2);
317721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
318721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        mDataFile0.setLength(0);
319721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        mDataFile1.setLength(0);
320721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        mDataFile0.seek(0);
321721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        mDataFile1.seek(0);
322721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        writeInt(buf, 0, MAGIC_DATA_FILE);
323721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        mDataFile0.write(buf, 0, 4);
324721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        mDataFile1.write(buf, 0, 4);
325721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    }
326721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
327721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // Flip the active region and the inactive region.
328721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private void flipRegion() throws IOException {
329721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        mActiveRegion = 1 - mActiveRegion;
330721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        mActiveEntries = 0;
331721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        mActiveBytes = DATA_HEADER_SIZE;
332721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
333721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        writeInt(mIndexHeader, IH_ACTIVE_REGION, mActiveRegion);
334721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        writeInt(mIndexHeader, IH_ACTIVE_ENTRIES, mActiveEntries);
335721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        writeInt(mIndexHeader, IH_ACTIVE_BYTES, mActiveBytes);
336721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        updateIndexHeader();
337721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
338721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        setActiveVariables();
339721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        clearHash(mActiveHashStart);
340721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        syncIndex();
341721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    }
342721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
343721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // Sync mIndexHeader to the index file.
344721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private void updateIndexHeader() {
345721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        writeInt(mIndexHeader, IH_CHECKSUM,
346721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                checkSum(mIndexHeader, 0, IH_CHECKSUM));
347721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        mIndexBuffer.position(0);
348721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        mIndexBuffer.put(mIndexHeader);
349721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    }
350721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
351721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // Clear the hash table starting from the specified offset.
352721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private void clearHash(int hashStart) {
353721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        byte[] zero = new byte[1024];
354721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        mIndexBuffer.position(hashStart);
355721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        for (int count = mMaxEntries * 12; count > 0;) {
356721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            int todo = Math.min(count, 1024);
357721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            mIndexBuffer.put(zero, 0, todo);
358721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            count -= todo;
359721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        }
360721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    }
361721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
362721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // Inserts a (key, data) pair into the cache.
363721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    public void insert(long key, byte[] data) throws IOException {
364721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        if (DATA_HEADER_SIZE + BLOB_HEADER_SIZE + data.length > mMaxBytes) {
365721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            throw new RuntimeException("blob is too large!");
366721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        }
367721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
368721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        if (mActiveBytes + BLOB_HEADER_SIZE + data.length > mMaxBytes
369721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                || mActiveEntries * 2 >= mMaxEntries) {
370721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            flipRegion();
371721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        }
372721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
373721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        if (!lookupInternal(key, mActiveHashStart)) {
374721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            // If we don't have an existing entry with the same key, increase
375721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            // the entry count.
376721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            mActiveEntries++;
377721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            writeInt(mIndexHeader, IH_ACTIVE_ENTRIES, mActiveEntries);
378721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        }
379721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
380721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        insertInternal(key, data, data.length);
381721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        updateIndexHeader();
382721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    }
383721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
384721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // Appends the data to the active file. It also updates the hash entry.
385721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // The proper hash entry (suitable for insertion or replacement) must be
386721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // pointed by mSlotOffset.
387721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private void insertInternal(long key, byte[] data, int length)
388721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            throws IOException {
389721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        byte[] header = mBlobHeader;
390721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        int sum = checkSum(data);
391721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        writeLong(header, BH_KEY, key);
392721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        writeInt(header, BH_CHECKSUM, sum);
393721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        writeInt(header, BH_OFFSET, mActiveBytes);
394721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        writeInt(header, BH_LENGTH, length);
395721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        mActiveDataFile.write(header);
396721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        mActiveDataFile.write(data, 0, length);
397721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
398721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        mIndexBuffer.putLong(mSlotOffset, key);
399721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        mIndexBuffer.putInt(mSlotOffset + 8, mActiveBytes);
400721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        mActiveBytes += BLOB_HEADER_SIZE + length;
401721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        writeInt(mIndexHeader, IH_ACTIVE_BYTES, mActiveBytes);
402721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    }
403721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
404721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    public static class LookupRequest {
405721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        public long key;        // input: the key to find
406721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        public byte[] buffer;   // input/output: the buffer to store the blob
407721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        public int length;      // output: the length of the blob
408721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    }
409721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
410721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // This method is for one-off lookup. For repeated lookup, use the version
411721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // accepting LookupRequest to avoid repeated memory allocation.
412721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private LookupRequest mLookupRequest = new LookupRequest();
413721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    public byte[] lookup(long key) throws IOException {
414721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        mLookupRequest.key = key;
415721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        mLookupRequest.buffer = null;
416721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        if (lookup(mLookupRequest)) {
417721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            return mLookupRequest.buffer;
418721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        } else {
419721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            return null;
420721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        }
421721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    }
422721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
423721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // Returns true if the associated blob for the given key is available.
424721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // The blob is stored in the buffer pointed by req.buffer, and the length
425721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // is in stored in the req.length variable.
426721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    //
427721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // The user can input a non-null value in req.buffer, and this method will
428721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // try to use that buffer. If that buffer is not large enough, this method
429721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // will allocate a new buffer and assign it to req.buffer.
430721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    //
431721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // This method tries not to throw IOException even if the data file is
432721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // corrupted, but it can still throw IOException if things get strange.
433721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    public boolean lookup(LookupRequest req) throws IOException {
434721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        // Look up in the active region first.
435721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        if (lookupInternal(req.key, mActiveHashStart)) {
436721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            if (getBlob(mActiveDataFile, mFileOffset, req)) {
437721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                return true;
438721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            }
439721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        }
440721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
441721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        // We want to copy the data from the inactive file to the active file
442721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        // if it's available. So we keep the offset of the hash entry so we can
443721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        // avoid looking it up again.
444721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        int insertOffset = mSlotOffset;
445721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
446721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        // Look up in the inactive region.
447721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        if (lookupInternal(req.key, mInactiveHashStart)) {
448721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            if (getBlob(mInactiveDataFile, mFileOffset, req)) {
449721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                // If we don't have enough space to insert this blob into
450721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                // the active file, just return it.
451721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                if (mActiveBytes + BLOB_HEADER_SIZE + req.length > mMaxBytes
452721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                    || mActiveEntries * 2 >= mMaxEntries) {
453721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                    return true;
454721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                }
455721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                // Otherwise copy it over.
456721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                mSlotOffset = insertOffset;
457721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                try {
458721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                    insertInternal(req.key, req.buffer, req.length);
459721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                    mActiveEntries++;
460721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                    writeInt(mIndexHeader, IH_ACTIVE_ENTRIES, mActiveEntries);
461721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                    updateIndexHeader();
462721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                } catch (Throwable t) {
463721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                    Log.e(TAG, "cannot copy over");
464721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                }
465721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                return true;
466721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            }
467721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        }
468721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
469721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        return false;
470721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    }
471721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
472721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
473721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // Copies the blob for the specified offset in the specified file to
474721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // req.buffer. If req.buffer is null or too small, allocate a buffer and
475721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // assign it to req.buffer.
476721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // Returns false if the blob is not available (either the index file is
477721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // not sync with the data file, or one of them is corrupted). The length
478721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // of the blob is stored in the req.length variable.
479721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private boolean getBlob(RandomAccessFile file, int offset,
480721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            LookupRequest req) throws IOException {
481721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        byte[] header = mBlobHeader;
482721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        long oldPosition = file.getFilePointer();
483721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        try {
484721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            file.seek(offset);
485721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            if (file.read(header) != BLOB_HEADER_SIZE) {
486721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                Log.w(TAG, "cannot read blob header");
487721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                return false;
488721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            }
489721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            long blobKey = readLong(header, BH_KEY);
490721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            if (blobKey != req.key) {
491721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                Log.w(TAG, "blob key does not match: " + blobKey);
492721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                return false;
493721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            }
494721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            int sum = readInt(header, BH_CHECKSUM);
495721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            int blobOffset = readInt(header, BH_OFFSET);
496721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            if (blobOffset != offset) {
497721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                Log.w(TAG, "blob offset does not match: " + blobOffset);
498721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                return false;
499721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            }
500721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            int length = readInt(header, BH_LENGTH);
501721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            if (length < 0 || length > mMaxBytes - offset - BLOB_HEADER_SIZE) {
502721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                Log.w(TAG, "invalid blob length: " + length);
503721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                return false;
504721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            }
505721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            if (req.buffer == null || req.buffer.length < length) {
506721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                req.buffer = new byte[length];
507721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            }
508721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
509721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            byte[] blob = req.buffer;
510721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            req.length = length;
511721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
512721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            if (file.read(blob, 0, length) != length) {
513721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                Log.w(TAG, "cannot read blob data");
514721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                return false;
515721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            }
516721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            if (checkSum(blob, 0, length) != sum) {
517721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                Log.w(TAG, "blob checksum does not match: " + sum);
518721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                return false;
519721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            }
520721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            return true;
521721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        } catch (Throwable t)  {
522721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            Log.e(TAG, "getBlob failed.", t);
523721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            return false;
524721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        } finally {
525721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            file.seek(oldPosition);
526721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        }
527721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    }
528721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
529721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // Tries to look up a key in the specified hash region.
530721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // Returns true if the lookup is successful.
531721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // The slot offset in the index file is saved in mSlotOffset. If the lookup
532721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // is successful, it's the slot found. Otherwise it's the slot suitable for
533721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // insertion.
534721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // If the lookup is successful, the file offset is also saved in
535721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // mFileOffset.
536721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private int mSlotOffset;
537721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private int mFileOffset;
538721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    private boolean lookupInternal(long key, int hashStart) {
539721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        int slot = (int) (key % mMaxEntries);
540721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        if (slot < 0) slot += mMaxEntries;
541721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        int slotBegin = slot;
542721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        while (true) {
543721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            int offset = hashStart + slot * 12;
544721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            long candidateKey = mIndexBuffer.getLong(offset);
545721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            int candidateOffset = mIndexBuffer.getInt(offset + 8);
546721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            if (candidateOffset == 0) {
547721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                mSlotOffset = offset;
548721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                return false;
549721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            } else if (candidateKey == key) {
550721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                mSlotOffset = offset;
551721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                mFileOffset = candidateOffset;
552721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                return true;
553721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            } else {
554721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                if (++slot >= mMaxEntries) {
555721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                    slot = 0;
556721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                }
557721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                if (slot == slotBegin) {
558721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                    Log.w(TAG, "corrupted index: clear the slot.");
559721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                    mIndexBuffer.putInt(hashStart + slot * 12 + 8, 0);
560721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                }
561721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            }
562721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        }
563721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    }
564721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
565721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    public void syncIndex() {
566721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        try {
567721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            mIndexBuffer.force();
568721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        } catch (Throwable t) {
569721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            Log.w(TAG, "sync index failed", t);
570721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        }
571721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    }
572721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
573721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    public void syncAll() {
574721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        syncIndex();
575721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        try {
576721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            mDataFile0.getFD().sync();
577721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        } catch (Throwable t) {
578721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            Log.w(TAG, "sync data file 0 failed", t);
579721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        }
580721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        try {
581721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            mDataFile1.getFD().sync();
582721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        } catch (Throwable t) {
583721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            Log.w(TAG, "sync data file 1 failed", t);
584721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        }
585721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    }
586721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
587721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // This is for testing only.
588721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    //
589721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // Returns the active count (mActiveEntries). This also verifies that
590721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    // the active count matches matches what's inside the hash region.
591721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    int getActiveCount() {
592721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        int count = 0;
593721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        for (int i = 0; i < mMaxEntries; i++) {
594721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            int offset = mActiveHashStart + i * 12;
595721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            long candidateKey = mIndexBuffer.getLong(offset);
596721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            int candidateOffset = mIndexBuffer.getInt(offset + 8);
597721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            if (candidateOffset != 0) ++count;
598721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        }
599721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        if (count == mActiveEntries) {
600721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            return count;
601721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        } else {
602721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            Log.e(TAG, "wrong active count: " + mActiveEntries + " vs " + count);
603721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            return -1;  // signal failure.
604721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        }
605721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    }
606721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
607721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    int checkSum(byte[] data) {
608721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        mAdler32.reset();
609721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        mAdler32.update(data);
610721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        return (int) mAdler32.getValue();
611721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    }
612721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
613721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    int checkSum(byte[] data, int offset, int nbytes) {
614721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        mAdler32.reset();
615721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        mAdler32.update(data, offset, nbytes);
616721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        return (int) mAdler32.getValue();
617721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    }
618721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
619721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    static void closeSilently(Closeable c) {
620721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        if (c == null) return;
621721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        try {
622721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            c.close();
623721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        } catch (Throwable t) {
624721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            // do nothing
625721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        }
626721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    }
627721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
628721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    static int readInt(byte[] buf, int offset) {
629721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        return (buf[offset] & 0xff)
630721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                | ((buf[offset + 1] & 0xff) << 8)
631721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                | ((buf[offset + 2] & 0xff) << 16)
632721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor                | ((buf[offset + 3] & 0xff) << 24);
633721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    }
634721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
635721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    static long readLong(byte[] buf, int offset) {
636721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        long result = buf[offset + 7] & 0xff;
637721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        for (int i = 6; i >= 0; i--) {
638721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            result = (result << 8) | (buf[offset + i] & 0xff);
639721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        }
640721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        return result;
641721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    }
642721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
643721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    static void writeInt(byte[] buf, int offset, int value) {
644721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        for (int i = 0; i < 4; i++) {
645721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            buf[offset + i] = (byte) (value & 0xff);
646721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            value >>= 8;
647721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        }
648721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    }
649721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor
650721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    static void writeLong(byte[] buf, int offset, long value) {
651721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        for (int i = 0; i < 8; i++) {
652721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            buf[offset + i] = (byte) (value & 0xff);
653721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor            value >>= 8;
654721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor        }
655721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor    }
656721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor}
657