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 android.util.Log; 69721ad07121cb9b0cd76bdbbc88494aa8f4d45a6dTom Taylor 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 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