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