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