1f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin/* 2f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Copyright (C) 2010 The Android Open Source Project 3f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * 4f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Licensed under the Apache License, Version 2.0 (the "License"); 5f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * you may not use this file except in compliance with the License. 6f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * You may obtain a copy of the License at 7f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * 8f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * http://www.apache.org/licenses/LICENSE-2.0 9f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * 10f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Unless required by applicable law or agreed to in writing, software 11f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * distributed under the License is distributed on an "AS IS" BASIS, 12f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * See the License for the specific language governing permissions and 14f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * limitations under the License. 15f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin */ 16f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 17f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// This is an on-disk cache which maps a 64-bits key to a byte array. 18f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// 19f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// It consists of three files: one index file and two data files. One of the 20f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// data files is "active", and the other is "inactive". New entries are 21f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// appended into the active region until it reaches the size limit. At that 22f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// point the active file and the inactive file are swapped, and the new active 23f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// file is truncated to empty (and the index for that file is also cleared). 24f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// The index is a hash table with linear probing. When the load factor reaches 25f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// 0.5, it does the same thing like when the size limit is reached. 26f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// 27f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// The index file format: (all numbers are stored in little-endian) 28f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// [0] Magic number: 0xB3273030 29f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// [4] MaxEntries: Max number of hash entries per region. 30f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// [8] MaxBytes: Max number of data bytes per region (including header). 31f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// [12] ActiveRegion: The active growing region: 0 or 1. 32f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// [16] ActiveEntries: The number of hash entries used in the active region. 33f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// [20] ActiveBytes: The number of data bytes used in the active region. 34f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// [24] Version number. 35f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// [28] Checksum of [0..28). 36f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// [32] Hash entries for region 0. The size is X = (12 * MaxEntries bytes). 37f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// [32 + X] Hash entries for region 1. The size is also X. 38f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// 39f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// Each hash entry is 12 bytes: 8 bytes key and 4 bytes offset into the data 40f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// file. The offset is 0 when the slot is free. Note that 0 is a valid value 41f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// for key. The keys are used directly as index into a hash table, so they 42f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// should be suitably distributed. 43f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// 44f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// Each data file stores data for one region. The data file is concatenated 45f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// blobs followed by the magic number 0xBD248510. 46f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// 47f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// The blob format: 48f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// [0] Key of this blob 49f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// [8] Checksum of this blob 50f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// [12] Offset of this blob 51f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// [16] Length of this blob (not including header) 52f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// [20] Blob 53f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// 54f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// Below are the interface for BlobCache. The instance of this class does not 55f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// support concurrent use by multiple threads. 56f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// 57f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// public BlobCache(String path, int maxEntries, int maxBytes, boolean reset) throws IOException; 58f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// public void insert(long key, byte[] data) throws IOException; 59f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// public byte[] lookup(long key) throws IOException; 60f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// public void lookup(LookupRequest req) throws IOException; 61f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// public void close(); 62f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// public void syncIndex(); 63f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// public void syncAll(); 64f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// public static void deleteFiles(String path); 65f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// 66f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linpackage com.android.gallery3d.common; 67f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 68f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.util.Log; 69f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 70f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.io.Closeable; 71f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.io.File; 72f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.io.IOException; 73f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.io.RandomAccessFile; 74f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.nio.ByteOrder; 75f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.nio.MappedByteBuffer; 76f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.nio.channels.FileChannel; 771c35ad56a274a8c1b6a60197855c0b838c4e1022George Mountimport java.util.Arrays; 78f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.zip.Adler32; 79f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 80c0715fd58c287347458756e34f07cee1fd72b982Owen Linpublic class BlobCache implements Closeable { 81f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final String TAG = "BlobCache"; 82f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 83f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final int MAGIC_INDEX_FILE = 0xB3273030; 84f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final int MAGIC_DATA_FILE = 0xBD248510; 85f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 86f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // index header offset 87f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final int IH_MAGIC = 0; 88f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final int IH_MAX_ENTRIES = 4; 89f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final int IH_MAX_BYTES = 8; 90f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final int IH_ACTIVE_REGION = 12; 91f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final int IH_ACTIVE_ENTRIES = 16; 92f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final int IH_ACTIVE_BYTES = 20; 93f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final int IH_VERSION = 24; 94f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final int IH_CHECKSUM = 28; 95f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final int INDEX_HEADER_SIZE = 32; 96f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 97f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final int DATA_HEADER_SIZE = 4; 98f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 99f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // blob header offset 100f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final int BH_KEY = 0; 101f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final int BH_CHECKSUM = 8; 102f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final int BH_OFFSET = 12; 103f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final int BH_LENGTH = 16; 104f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final int BLOB_HEADER_SIZE = 20; 105f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 106f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private RandomAccessFile mIndexFile; 107f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private RandomAccessFile mDataFile0; 108f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private RandomAccessFile mDataFile1; 109f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private FileChannel mIndexChannel; 110f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private MappedByteBuffer mIndexBuffer; 111f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 112f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private int mMaxEntries; 113f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private int mMaxBytes; 114f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private int mActiveRegion; 115f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private int mActiveEntries; 116f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private int mActiveBytes; 117f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private int mVersion; 118f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 119f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private RandomAccessFile mActiveDataFile; 120f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private RandomAccessFile mInactiveDataFile; 121f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private int mActiveHashStart; 122f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private int mInactiveHashStart; 123f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private byte[] mIndexHeader = new byte[INDEX_HEADER_SIZE]; 124f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private byte[] mBlobHeader = new byte[BLOB_HEADER_SIZE]; 125f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private Adler32 mAdler32 = new Adler32(); 126f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 127f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // Creates the cache. Three files will be created: 128f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // path + ".idx", path + ".0", and path + ".1" 129f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // The ".0" file and the ".1" file each stores data for a region. Each of 130f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // them can grow to the size specified by maxBytes. The maxEntries parameter 131f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // specifies the maximum number of entries each region can have. If the 132f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // "reset" parameter is true, the cache will be cleared before use. 133f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public BlobCache(String path, int maxEntries, int maxBytes, boolean reset) 134f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin throws IOException { 135f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin this(path, maxEntries, maxBytes, reset, 0); 136f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 137f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 138f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public BlobCache(String path, int maxEntries, int maxBytes, boolean reset, 139f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int version) throws IOException { 140f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mIndexFile = new RandomAccessFile(path + ".idx", "rw"); 141f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mDataFile0 = new RandomAccessFile(path + ".0", "rw"); 142f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mDataFile1 = new RandomAccessFile(path + ".1", "rw"); 143f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mVersion = version; 144f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 145f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (!reset && loadIndex()) { 146f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return; 147f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 148f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 149f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin resetCache(maxEntries, maxBytes); 150f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 151f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (!loadIndex()) { 152f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin closeAll(); 153f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin throw new IOException("unable to load index"); 154f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 155f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 156f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 157f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // Delete the files associated with the given path previously created 158f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // by the BlobCache constructor. 159f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public static void deleteFiles(String path) { 160f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin deleteFileSilently(path + ".idx"); 161f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin deleteFileSilently(path + ".0"); 162f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin deleteFileSilently(path + ".1"); 163f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 164f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 165f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static void deleteFileSilently(String path) { 166f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin try { 167f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin new File(path).delete(); 168f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } catch (Throwable t) { 169f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // ignore; 170f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 171f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 172f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 173f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // Close the cache. All resources are released. No other method should be 174f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // called after this is called. 175c0715fd58c287347458756e34f07cee1fd72b982Owen Lin @Override 176f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void close() { 177f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin syncAll(); 178f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin closeAll(); 179f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 180f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 181f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private void closeAll() { 182f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin closeSilently(mIndexChannel); 183f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin closeSilently(mIndexFile); 184f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin closeSilently(mDataFile0); 185f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin closeSilently(mDataFile1); 186f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 187f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 188f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // Returns true if loading index is successful. After this method is called, 189f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // mIndexHeader and index header in file should be kept sync. 190f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private boolean loadIndex() { 191f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin try { 192f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mIndexFile.seek(0); 193f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mDataFile0.seek(0); 194f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mDataFile1.seek(0); 195f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 196f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin byte[] buf = mIndexHeader; 197f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mIndexFile.read(buf) != INDEX_HEADER_SIZE) { 198f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Log.w(TAG, "cannot read header"); 199f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return false; 200f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 201f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 202f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (readInt(buf, IH_MAGIC) != MAGIC_INDEX_FILE) { 203f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Log.w(TAG, "cannot read header magic"); 204f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return false; 205f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 206f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 207f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (readInt(buf, IH_VERSION) != mVersion) { 208f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Log.w(TAG, "version mismatch"); 209f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return false; 210f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 211f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 212f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mMaxEntries = readInt(buf, IH_MAX_ENTRIES); 213f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mMaxBytes = readInt(buf, IH_MAX_BYTES); 214f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mActiveRegion = readInt(buf, IH_ACTIVE_REGION); 215f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mActiveEntries = readInt(buf, IH_ACTIVE_ENTRIES); 216f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mActiveBytes = readInt(buf, IH_ACTIVE_BYTES); 217f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 218f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int sum = readInt(buf, IH_CHECKSUM); 219f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (checkSum(buf, 0, IH_CHECKSUM) != sum) { 220f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Log.w(TAG, "header checksum does not match"); 221f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return false; 222f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 223f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 224f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // Sanity check 225f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mMaxEntries <= 0) { 226f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Log.w(TAG, "invalid max entries"); 227f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return false; 228f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 229f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mMaxBytes <= 0) { 230f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Log.w(TAG, "invalid max bytes"); 231f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return false; 232f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 233f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mActiveRegion != 0 && mActiveRegion != 1) { 234f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Log.w(TAG, "invalid active region"); 235f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return false; 236f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 237f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mActiveEntries < 0 || mActiveEntries > mMaxEntries) { 238f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Log.w(TAG, "invalid active entries"); 239f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return false; 240f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 241f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mActiveBytes < DATA_HEADER_SIZE || mActiveBytes > mMaxBytes) { 242f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Log.w(TAG, "invalid active bytes"); 243f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return false; 244f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 245f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mIndexFile.length() != 246f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin INDEX_HEADER_SIZE + mMaxEntries * 12 * 2) { 247f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Log.w(TAG, "invalid index file length"); 248f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return false; 249f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 250f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 251f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // Make sure data file has magic 252f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin byte[] magic = new byte[4]; 253f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mDataFile0.read(magic) != 4) { 254f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Log.w(TAG, "cannot read data file magic"); 255f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return false; 256f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 257f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (readInt(magic, 0) != MAGIC_DATA_FILE) { 258f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Log.w(TAG, "invalid data file magic"); 259f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return false; 260f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 261f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mDataFile1.read(magic) != 4) { 262f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Log.w(TAG, "cannot read data file magic"); 263f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return false; 264f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 265f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (readInt(magic, 0) != MAGIC_DATA_FILE) { 266f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Log.w(TAG, "invalid data file magic"); 267f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return false; 268f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 269f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 270f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // Map index file to memory 271f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mIndexChannel = mIndexFile.getChannel(); 272f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mIndexBuffer = mIndexChannel.map(FileChannel.MapMode.READ_WRITE, 273f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 0, mIndexFile.length()); 274f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mIndexBuffer.order(ByteOrder.LITTLE_ENDIAN); 275f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 276f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin setActiveVariables(); 277f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return true; 278f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } catch (IOException ex) { 279f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Log.e(TAG, "loadIndex failed.", ex); 280f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return false; 281f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 282f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 283f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 284f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private void setActiveVariables() throws IOException { 285f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mActiveDataFile = (mActiveRegion == 0) ? mDataFile0 : mDataFile1; 286f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mInactiveDataFile = (mActiveRegion == 1) ? mDataFile0 : mDataFile1; 287f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mActiveDataFile.setLength(mActiveBytes); 288f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mActiveDataFile.seek(mActiveBytes); 289f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 290f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mActiveHashStart = INDEX_HEADER_SIZE; 291f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mInactiveHashStart = INDEX_HEADER_SIZE; 292f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 293f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mActiveRegion == 0) { 294f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mInactiveHashStart += mMaxEntries * 12; 295f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } else { 296f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mActiveHashStart += mMaxEntries * 12; 297f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 298f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 299f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 300f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private void resetCache(int maxEntries, int maxBytes) throws IOException { 301f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mIndexFile.setLength(0); // truncate to zero the index 302f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mIndexFile.setLength(INDEX_HEADER_SIZE + maxEntries * 12 * 2); 303f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mIndexFile.seek(0); 304f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin byte[] buf = mIndexHeader; 305f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin writeInt(buf, IH_MAGIC, MAGIC_INDEX_FILE); 306f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin writeInt(buf, IH_MAX_ENTRIES, maxEntries); 307f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin writeInt(buf, IH_MAX_BYTES, maxBytes); 308f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin writeInt(buf, IH_ACTIVE_REGION, 0); 309f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin writeInt(buf, IH_ACTIVE_ENTRIES, 0); 310f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin writeInt(buf, IH_ACTIVE_BYTES, DATA_HEADER_SIZE); 311f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin writeInt(buf, IH_VERSION, mVersion); 312f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin writeInt(buf, IH_CHECKSUM, checkSum(buf, 0, IH_CHECKSUM)); 313f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mIndexFile.write(buf); 314f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // This is only needed if setLength does not zero the extended part. 315f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // writeZero(mIndexFile, maxEntries * 12 * 2); 316f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 317f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mDataFile0.setLength(0); 318f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mDataFile1.setLength(0); 319f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mDataFile0.seek(0); 320f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mDataFile1.seek(0); 321f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin writeInt(buf, 0, MAGIC_DATA_FILE); 322f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mDataFile0.write(buf, 0, 4); 323f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mDataFile1.write(buf, 0, 4); 324f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 325f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 326f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // Flip the active region and the inactive region. 327f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private void flipRegion() throws IOException { 328f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mActiveRegion = 1 - mActiveRegion; 329f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mActiveEntries = 0; 330f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mActiveBytes = DATA_HEADER_SIZE; 331f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 332f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin writeInt(mIndexHeader, IH_ACTIVE_REGION, mActiveRegion); 333f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin writeInt(mIndexHeader, IH_ACTIVE_ENTRIES, mActiveEntries); 334f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin writeInt(mIndexHeader, IH_ACTIVE_BYTES, mActiveBytes); 335f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin updateIndexHeader(); 336f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 337f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin setActiveVariables(); 338f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin clearHash(mActiveHashStart); 339f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin syncIndex(); 340f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 341f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 342f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // Sync mIndexHeader to the index file. 343f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private void updateIndexHeader() { 344f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin writeInt(mIndexHeader, IH_CHECKSUM, 345f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin checkSum(mIndexHeader, 0, IH_CHECKSUM)); 346f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mIndexBuffer.position(0); 347f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mIndexBuffer.put(mIndexHeader); 348f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 349f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 350f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // Clear the hash table starting from the specified offset. 351f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private void clearHash(int hashStart) { 352f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin byte[] zero = new byte[1024]; 353f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mIndexBuffer.position(hashStart); 354f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin for (int count = mMaxEntries * 12; count > 0;) { 355f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int todo = Math.min(count, 1024); 356f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mIndexBuffer.put(zero, 0, todo); 357f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin count -= todo; 358f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 359f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 360f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 361f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // Inserts a (key, data) pair into the cache. 362f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void insert(long key, byte[] data) throws IOException { 363f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (DATA_HEADER_SIZE + BLOB_HEADER_SIZE + data.length > mMaxBytes) { 364f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin throw new RuntimeException("blob is too large!"); 365f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 366f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 367f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mActiveBytes + BLOB_HEADER_SIZE + data.length > mMaxBytes 368f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin || mActiveEntries * 2 >= mMaxEntries) { 369f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin flipRegion(); 370f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 371f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 372f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (!lookupInternal(key, mActiveHashStart)) { 373f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // If we don't have an existing entry with the same key, increase 374f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // the entry count. 375f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mActiveEntries++; 376f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin writeInt(mIndexHeader, IH_ACTIVE_ENTRIES, mActiveEntries); 377f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 378f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 379f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin insertInternal(key, data, data.length); 380f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin updateIndexHeader(); 381f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 382f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 3831c35ad56a274a8c1b6a60197855c0b838c4e1022George Mount public void clearEntry(long key) throws IOException { 3841c35ad56a274a8c1b6a60197855c0b838c4e1022George Mount if (!lookupInternal(key, mActiveHashStart)) { 3851c35ad56a274a8c1b6a60197855c0b838c4e1022George Mount return; // Nothing to clear 3861c35ad56a274a8c1b6a60197855c0b838c4e1022George Mount } 3871c35ad56a274a8c1b6a60197855c0b838c4e1022George Mount byte[] header = mBlobHeader; 3881c35ad56a274a8c1b6a60197855c0b838c4e1022George Mount Arrays.fill(header, (byte) 0); 3891c35ad56a274a8c1b6a60197855c0b838c4e1022George Mount mActiveDataFile.seek(mFileOffset); 3901c35ad56a274a8c1b6a60197855c0b838c4e1022George Mount mActiveDataFile.write(header); 3911c35ad56a274a8c1b6a60197855c0b838c4e1022George Mount } 3921c35ad56a274a8c1b6a60197855c0b838c4e1022George Mount 393f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // Appends the data to the active file. It also updates the hash entry. 394f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // The proper hash entry (suitable for insertion or replacement) must be 395f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // pointed by mSlotOffset. 396f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private void insertInternal(long key, byte[] data, int length) 397f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin throws IOException { 398f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin byte[] header = mBlobHeader; 399f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int sum = checkSum(data); 400f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin writeLong(header, BH_KEY, key); 401f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin writeInt(header, BH_CHECKSUM, sum); 402f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin writeInt(header, BH_OFFSET, mActiveBytes); 403f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin writeInt(header, BH_LENGTH, length); 404f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mActiveDataFile.write(header); 405f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mActiveDataFile.write(data, 0, length); 406f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 407f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mIndexBuffer.putLong(mSlotOffset, key); 408f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mIndexBuffer.putInt(mSlotOffset + 8, mActiveBytes); 409f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mActiveBytes += BLOB_HEADER_SIZE + length; 410f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin writeInt(mIndexHeader, IH_ACTIVE_BYTES, mActiveBytes); 411f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 412f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 413f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public static class LookupRequest { 414f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public long key; // input: the key to find 415f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public byte[] buffer; // input/output: the buffer to store the blob 416f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public int length; // output: the length of the blob 417f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 418f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 419f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // This method is for one-off lookup. For repeated lookup, use the version 420f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // accepting LookupRequest to avoid repeated memory allocation. 421f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private LookupRequest mLookupRequest = new LookupRequest(); 422f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public byte[] lookup(long key) throws IOException { 423f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mLookupRequest.key = key; 424f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mLookupRequest.buffer = null; 425f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (lookup(mLookupRequest)) { 426f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return mLookupRequest.buffer; 427f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } else { 428f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return null; 429f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 430f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 431f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 432f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // Returns true if the associated blob for the given key is available. 433f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // The blob is stored in the buffer pointed by req.buffer, and the length 434f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // is in stored in the req.length variable. 435f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // 436f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // The user can input a non-null value in req.buffer, and this method will 437f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // try to use that buffer. If that buffer is not large enough, this method 438f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // will allocate a new buffer and assign it to req.buffer. 439f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // 440f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // This method tries not to throw IOException even if the data file is 441f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // corrupted, but it can still throw IOException if things get strange. 442f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public boolean lookup(LookupRequest req) throws IOException { 443f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // Look up in the active region first. 444f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (lookupInternal(req.key, mActiveHashStart)) { 445f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (getBlob(mActiveDataFile, mFileOffset, req)) { 446f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return true; 447f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 448f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 449f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 450f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // We want to copy the data from the inactive file to the active file 451f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // if it's available. So we keep the offset of the hash entry so we can 452f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // avoid looking it up again. 453f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int insertOffset = mSlotOffset; 454f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 455f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // Look up in the inactive region. 456f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (lookupInternal(req.key, mInactiveHashStart)) { 457f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (getBlob(mInactiveDataFile, mFileOffset, req)) { 458f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // If we don't have enough space to insert this blob into 459f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // the active file, just return it. 460f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (mActiveBytes + BLOB_HEADER_SIZE + req.length > mMaxBytes 461f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin || mActiveEntries * 2 >= mMaxEntries) { 462f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return true; 463f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 464f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // Otherwise copy it over. 465f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mSlotOffset = insertOffset; 466f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin try { 467f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin insertInternal(req.key, req.buffer, req.length); 468f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mActiveEntries++; 469f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin writeInt(mIndexHeader, IH_ACTIVE_ENTRIES, mActiveEntries); 470f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin updateIndexHeader(); 471f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } catch (Throwable t) { 472f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Log.e(TAG, "cannot copy over"); 473f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 474f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return true; 475f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 476f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 477f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 478f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return false; 479f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 480f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 481f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 482f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // Copies the blob for the specified offset in the specified file to 483f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // req.buffer. If req.buffer is null or too small, allocate a buffer and 484f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // assign it to req.buffer. 485f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // Returns false if the blob is not available (either the index file is 486f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // not sync with the data file, or one of them is corrupted). The length 487f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // of the blob is stored in the req.length variable. 488f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private boolean getBlob(RandomAccessFile file, int offset, 489f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin LookupRequest req) throws IOException { 490f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin byte[] header = mBlobHeader; 491f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin long oldPosition = file.getFilePointer(); 492f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin try { 493f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin file.seek(offset); 494f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (file.read(header) != BLOB_HEADER_SIZE) { 495f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Log.w(TAG, "cannot read blob header"); 496f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return false; 497f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 498f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin long blobKey = readLong(header, BH_KEY); 4991c35ad56a274a8c1b6a60197855c0b838c4e1022George Mount if (blobKey == 0) { 5001c35ad56a274a8c1b6a60197855c0b838c4e1022George Mount return false; // This entry has been cleared. 5011c35ad56a274a8c1b6a60197855c0b838c4e1022George Mount } 502f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (blobKey != req.key) { 503f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Log.w(TAG, "blob key does not match: " + blobKey); 504f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return false; 505f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 506f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int sum = readInt(header, BH_CHECKSUM); 507f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int blobOffset = readInt(header, BH_OFFSET); 508f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (blobOffset != offset) { 509f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Log.w(TAG, "blob offset does not match: " + blobOffset); 510f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return false; 511f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 512f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int length = readInt(header, BH_LENGTH); 513f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (length < 0 || length > mMaxBytes - offset - BLOB_HEADER_SIZE) { 514f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Log.w(TAG, "invalid blob length: " + length); 515f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return false; 516f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 517f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (req.buffer == null || req.buffer.length < length) { 518f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin req.buffer = new byte[length]; 519f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 520f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 521f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin byte[] blob = req.buffer; 522f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin req.length = length; 523f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 524f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (file.read(blob, 0, length) != length) { 525f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Log.w(TAG, "cannot read blob data"); 526f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return false; 527f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 528f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (checkSum(blob, 0, length) != sum) { 529f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Log.w(TAG, "blob checksum does not match: " + sum); 530f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return false; 531f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 532f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return true; 533f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } catch (Throwable t) { 534f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Log.e(TAG, "getBlob failed.", t); 535f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return false; 536f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } finally { 537f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin file.seek(oldPosition); 538f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 539f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 540f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 541f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // Tries to look up a key in the specified hash region. 542f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // Returns true if the lookup is successful. 543f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // The slot offset in the index file is saved in mSlotOffset. If the lookup 544f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // is successful, it's the slot found. Otherwise it's the slot suitable for 545f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // insertion. 546f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // If the lookup is successful, the file offset is also saved in 547f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // mFileOffset. 548f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private int mSlotOffset; 549f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private int mFileOffset; 550f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private boolean lookupInternal(long key, int hashStart) { 551f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int slot = (int) (key % mMaxEntries); 552f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (slot < 0) slot += mMaxEntries; 553f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int slotBegin = slot; 554f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin while (true) { 555f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int offset = hashStart + slot * 12; 556f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin long candidateKey = mIndexBuffer.getLong(offset); 557f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int candidateOffset = mIndexBuffer.getInt(offset + 8); 558f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (candidateOffset == 0) { 559f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mSlotOffset = offset; 560f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return false; 561f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } else if (candidateKey == key) { 562f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mSlotOffset = offset; 563f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mFileOffset = candidateOffset; 564f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return true; 565f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } else { 566f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (++slot >= mMaxEntries) { 567f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin slot = 0; 568f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 569f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (slot == slotBegin) { 570f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Log.w(TAG, "corrupted index: clear the slot."); 571f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mIndexBuffer.putInt(hashStart + slot * 12 + 8, 0); 572f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 573f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 574f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 575f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 576f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 577f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void syncIndex() { 578f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin try { 579f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mIndexBuffer.force(); 580f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } catch (Throwable t) { 581f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Log.w(TAG, "sync index failed", t); 582f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 583f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 584f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 585f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void syncAll() { 586f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin syncIndex(); 587f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin try { 588f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mDataFile0.getFD().sync(); 589f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } catch (Throwable t) { 590f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Log.w(TAG, "sync data file 0 failed", t); 591f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 592f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin try { 593f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mDataFile1.getFD().sync(); 594f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } catch (Throwable t) { 595f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Log.w(TAG, "sync data file 1 failed", t); 596f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 597f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 598f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 599f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // This is for testing only. 600f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // 601f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // Returns the active count (mActiveEntries). This also verifies that 602f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // the active count matches matches what's inside the hash region. 603f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int getActiveCount() { 604f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int count = 0; 605f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin for (int i = 0; i < mMaxEntries; i++) { 606f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int offset = mActiveHashStart + i * 12; 607f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin long candidateKey = mIndexBuffer.getLong(offset); 608f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int candidateOffset = mIndexBuffer.getInt(offset + 8); 609f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (candidateOffset != 0) ++count; 610f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 611f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (count == mActiveEntries) { 612f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return count; 613f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } else { 614f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Log.e(TAG, "wrong active count: " + mActiveEntries + " vs " + count); 615f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return -1; // signal failure. 616f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 617f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 618f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 619f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int checkSum(byte[] data) { 620f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mAdler32.reset(); 621f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mAdler32.update(data); 622f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return (int) mAdler32.getValue(); 623f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 624f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 625f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin int checkSum(byte[] data, int offset, int nbytes) { 626f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mAdler32.reset(); 627f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mAdler32.update(data, offset, nbytes); 628f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return (int) mAdler32.getValue(); 629f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 630f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 631f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin static void closeSilently(Closeable c) { 632f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (c == null) return; 633f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin try { 634f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin c.close(); 635f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } catch (Throwable t) { 636f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // do nothing 637f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 638f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 639f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 640f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin static int readInt(byte[] buf, int offset) { 641f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return (buf[offset] & 0xff) 642f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin | ((buf[offset + 1] & 0xff) << 8) 643f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin | ((buf[offset + 2] & 0xff) << 16) 644f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin | ((buf[offset + 3] & 0xff) << 24); 645f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 646f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 647f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin static long readLong(byte[] buf, int offset) { 648f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin long result = buf[offset + 7] & 0xff; 649f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin for (int i = 6; i >= 0; i--) { 650f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin result = (result << 8) | (buf[offset + i] & 0xff); 651f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 652f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return result; 653f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 654f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 655f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin static void writeInt(byte[] buf, int offset, int value) { 656f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin for (int i = 0; i < 4; i++) { 657f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin buf[offset + i] = (byte) (value & 0xff); 658f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin value >>= 8; 659f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 660f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 661f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 662f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin static void writeLong(byte[] buf, int offset, long value) { 663f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin for (int i = 0; i < 8; i++) { 664f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin buf[offset + i] = (byte) (value & 0xff); 665f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin value >>= 8; 666f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 667f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 668f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin} 669