12bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch/* 203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * Copyright (C) 2011 The Android Open Source Project 32bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch * 42bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch * Licensed under the Apache License, Version 2.0 (the "License"); 52bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch * you may not use this file except in compliance with the License. 62bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch * You may obtain a copy of the License at 72bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch * 82bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch * http://www.apache.org/licenses/LICENSE-2.0 92bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch * 102bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch * Unless required by applicable law or agreed to in writing, software 112bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch * distributed under the License is distributed on an "AS IS" BASIS, 122bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 132bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch * See the License for the specific language governing permissions and 142bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch * limitations under the License. 152bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch */ 162bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch 172bab0137e0af92c374d3efb2ed0e1d894981e015Adam Kochpackage com.example.android.bitmapfun.util; 182bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch 1903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Kochimport java.io.BufferedInputStream; 2003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Kochimport java.io.BufferedWriter; 2103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Kochimport java.io.Closeable; 2203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Kochimport java.io.EOFException; 232bab0137e0af92c374d3efb2ed0e1d894981e015Adam Kochimport java.io.File; 2403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Kochimport java.io.FileInputStream; 252bab0137e0af92c374d3efb2ed0e1d894981e015Adam Kochimport java.io.FileNotFoundException; 262bab0137e0af92c374d3efb2ed0e1d894981e015Adam Kochimport java.io.FileOutputStream; 2703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Kochimport java.io.FileWriter; 2803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Kochimport java.io.FilterOutputStream; 292bab0137e0af92c374d3efb2ed0e1d894981e015Adam Kochimport java.io.IOException; 3003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Kochimport java.io.InputStream; 3103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Kochimport java.io.InputStreamReader; 322bab0137e0af92c374d3efb2ed0e1d894981e015Adam Kochimport java.io.OutputStream; 3303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Kochimport java.io.OutputStreamWriter; 3403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Kochimport java.io.Reader; 3503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Kochimport java.io.StringWriter; 3603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Kochimport java.io.Writer; 3703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Kochimport java.lang.reflect.Array; 3803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Kochimport java.nio.charset.Charset; 3903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Kochimport java.util.ArrayList; 4003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Kochimport java.util.Arrays; 4103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Kochimport java.util.Iterator; 422bab0137e0af92c374d3efb2ed0e1d894981e015Adam Kochimport java.util.LinkedHashMap; 432bab0137e0af92c374d3efb2ed0e1d894981e015Adam Kochimport java.util.Map; 4403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Kochimport java.util.concurrent.Callable; 4503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Kochimport java.util.concurrent.ExecutorService; 4603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Kochimport java.util.concurrent.LinkedBlockingQueue; 4703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Kochimport java.util.concurrent.ThreadPoolExecutor; 4803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Kochimport java.util.concurrent.TimeUnit; 492bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch 502bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch/** 5103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch ****************************************************************************** 5203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * Taken from the JB source code, can be found in: 5303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * libcore/luni/src/main/java/libcore/io/DiskLruCache.java 5403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * or direct link: 5503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * https://android.googlesource.com/platform/libcore/+/android-4.1.1_r1/luni/src/main/java/libcore/io/DiskLruCache.java 5603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch ****************************************************************************** 5703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * 5803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * A cache that uses a bounded amount of space on a filesystem. Each cache 5903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * entry has a string key and a fixed number of values. Values are byte 6003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * sequences, accessible as streams or files. Each value must be between {@code 6103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * 0} and {@code Integer.MAX_VALUE} bytes in length. 6203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * 6303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * <p>The cache stores its data in a directory on the filesystem. This 6403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * directory must be exclusive to the cache; the cache may delete or overwrite 6503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * files from its directory. It is an error for multiple processes to use the 6603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * same cache directory at the same time. 6703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * 6803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * <p>This cache limits the number of bytes that it will store on the 6903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * filesystem. When the number of stored bytes exceeds the limit, the cache will 7003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * remove entries in the background until the limit is satisfied. The limit is 7103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * not strict: the cache may temporarily exceed it while waiting for files to be 7203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * deleted. The limit does not include filesystem overhead or the cache 7303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * journal so space-sensitive applications should set a conservative limit. 7403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * 7503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * <p>Clients call {@link #edit} to create or update the values of an entry. An 7603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * entry may have only one editor at one time; if a value is not available to be 7703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * edited then {@link #edit} will return null. 7803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * <ul> 7903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * <li>When an entry is being <strong>created</strong> it is necessary to 8003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * supply a full set of values; the empty value should be used as a 8103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * placeholder if necessary. 8203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * <li>When an entry is being <strong>edited</strong>, it is not necessary 8303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * to supply data for every value; values default to their previous 8403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * value. 8503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * </ul> 8603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * Every {@link #edit} call must be matched by a call to {@link Editor#commit} 8703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * or {@link Editor#abort}. Committing is atomic: a read observes the full set 8803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * of values as they were before or after the commit, but never a mix of values. 8903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * 9003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * <p>Clients call {@link #get} to read a snapshot of an entry. The read will 9103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * observe the value at the time that {@link #get} was called. Updates and 9203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * removals after the call do not impact ongoing reads. 9303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * 9403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * <p>This class is tolerant of some I/O errors. If files are missing from the 9503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * filesystem, the corresponding entries will be dropped from the cache. If 9603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * an error occurs while writing a cache value, the edit will fail silently. 9703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * Callers should handle other problems by catching {@code IOException} and 9803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * responding appropriately. 992bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch */ 10003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Kochpublic final class DiskLruCache implements Closeable { 10103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch static final String JOURNAL_FILE = "journal"; 10203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch static final String JOURNAL_FILE_TMP = "journal.tmp"; 10303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch static final String MAGIC = "libcore.io.DiskLruCache"; 10403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch static final String VERSION_1 = "1"; 10503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch static final long ANY_SEQUENCE_NUMBER = -1; 10603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private static final String CLEAN = "CLEAN"; 10703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private static final String DIRTY = "DIRTY"; 10803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private static final String REMOVE = "REMOVE"; 10903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private static final String READ = "READ"; 11003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 11103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private static final Charset UTF_8 = Charset.forName("UTF-8"); 11203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private static final int IO_BUFFER_SIZE = 8 * 1024; 11303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 11403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch /* 11503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * This cache uses a journal file named "journal". A typical journal file 11603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * looks like this: 11703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * libcore.io.DiskLruCache 11803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * 1 11903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * 100 12003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * 2 12103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * 12203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * CLEAN 3400330d1dfc7f3f7f4b8d4d803dfcf6 832 21054 12303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * DIRTY 335c4c6028171cfddfbaae1a9c313c52 12403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * CLEAN 335c4c6028171cfddfbaae1a9c313c52 3934 2342 12503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * REMOVE 335c4c6028171cfddfbaae1a9c313c52 12603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * DIRTY 1ab96a171faeeee38496d8b330771a7a 12703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * CLEAN 1ab96a171faeeee38496d8b330771a7a 1600 234 12803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * READ 335c4c6028171cfddfbaae1a9c313c52 12903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * READ 3400330d1dfc7f3f7f4b8d4d803dfcf6 13003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * 13103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * The first five lines of the journal form its header. They are the 13203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * constant string "libcore.io.DiskLruCache", the disk cache's version, 13303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * the application's version, the value count, and a blank line. 13403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * 13503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * Each of the subsequent lines in the file is a record of the state of a 13603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * cache entry. Each line contains space-separated values: a state, a key, 13703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * and optional state-specific values. 13803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * o DIRTY lines track that an entry is actively being created or updated. 13903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * Every successful DIRTY action should be followed by a CLEAN or REMOVE 14003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * action. DIRTY lines without a matching CLEAN or REMOVE indicate that 14103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * temporary files may need to be deleted. 14203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * o CLEAN lines track a cache entry that has been successfully published 14303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * and may be read. A publish line is followed by the lengths of each of 14403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * its values. 14503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * o READ lines track accesses for LRU. 14603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * o REMOVE lines track entries that have been deleted. 14703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * 14803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * The journal file is appended to as cache operations occur. The journal may 14903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * occasionally be compacted by dropping redundant lines. A temporary file named 15003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * "journal.tmp" will be used during compaction; that file should be deleted if 15103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * it exists when the cache is opened. 15203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch */ 15303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 15403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private final File directory; 15503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private final File journalFile; 15603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private final File journalFileTmp; 15703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private final int appVersion; 15803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private final long maxSize; 15903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private final int valueCount; 16003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private long size = 0; 16103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private Writer journalWriter; 16203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private final LinkedHashMap<String, Entry> lruEntries 16303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch = new LinkedHashMap<String, Entry>(0, 0.75f, true); 16403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private int redundantOpCount; 1652bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch 1662bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch /** 16703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * To differentiate between old and current snapshots, each entry is given 16803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * a sequence number each time an edit is committed. A snapshot is stale if 16903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * its sequence number is not equal to its entry's sequence number. 1702bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch */ 17103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private long nextSequenceNumber = 0; 17203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 17303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch /* From java.util.Arrays */ 17403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch @SuppressWarnings("unchecked") 17503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private static <T> T[] copyOfRange(T[] original, int start, int end) { 17603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch final int originalLength = original.length; // For exception priority compatibility. 17703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (start > end) { 17803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch throw new IllegalArgumentException(); 1792bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch } 18003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (start < 0 || start > originalLength) { 18103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch throw new ArrayIndexOutOfBoundsException(); 18203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 18303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch final int resultLength = end - start; 18403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch final int copyLength = Math.min(resultLength, originalLength - start); 18503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch final T[] result = (T[]) Array 18603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch .newInstance(original.getClass().getComponentType(), resultLength); 18703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch System.arraycopy(original, start, result, 0, copyLength); 18803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch return result; 18903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 1902bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch 1912bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch /** 19203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * Returns the remainder of 'reader' as a string, closing it when done. 1932bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch */ 19403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch public static String readFully(Reader reader) throws IOException { 19503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch try { 19603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch StringWriter writer = new StringWriter(); 19703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch char[] buffer = new char[1024]; 19803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch int count; 19903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch while ((count = reader.read(buffer)) != -1) { 20003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch writer.write(buffer, 0, count); 20103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 20203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch return writer.toString(); 20303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } finally { 20403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch reader.close(); 2052bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch } 20603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 20703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 20803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch /** 20903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * Returns the ASCII characters up to but not including the next "\r\n", or 21003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * "\n". 21103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * 21203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * @throws java.io.EOFException if the stream is exhausted before the next newline 21303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * character. 21403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch */ 21503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch public static String readAsciiLine(InputStream in) throws IOException { 21603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch // TODO: support UTF-8 here instead 2172bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch 21803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch StringBuilder result = new StringBuilder(80); 21903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch while (true) { 22003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch int c = in.read(); 22103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (c == -1) { 22203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch throw new EOFException(); 22303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } else if (c == '\n') { 22403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch break; 22503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 22603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 22703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch result.append((char) c); 2282bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch } 22903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch int length = result.length(); 23003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (length > 0 && result.charAt(length - 1) == '\r') { 23103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch result.setLength(length - 1); 23203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 23303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch return result.toString(); 23403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 2352bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch 23603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch /** 23703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * Closes 'closeable', ignoring any checked exceptions. Does nothing if 'closeable' is null. 23803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch */ 23903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch public static void closeQuietly(Closeable closeable) { 24003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (closeable != null) { 24103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch try { 24203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch closeable.close(); 24303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } catch (RuntimeException rethrown) { 24403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch throw rethrown; 24503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } catch (Exception ignored) { 24603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 24703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 2482bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch } 2492bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch 2502bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch /** 25103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * Recursively delete everything in {@code dir}. 2522bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch */ 25303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch // TODO: this should specify paths as Strings rather than as Files 25403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch public static void deleteContents(File dir) throws IOException { 25503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch File[] files = dir.listFiles(); 25603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (files == null) { 25703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch throw new IllegalArgumentException("not a directory: " + dir); 25803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 25903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch for (File file : files) { 26003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (file.isDirectory()) { 26103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch deleteContents(file); 26203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 26303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (!file.delete()) { 26403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch throw new IOException("failed to delete file: " + file); 26503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 26603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 26703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 26803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 26903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch /** This cache uses a single background thread to evict entries. */ 27003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private final ExecutorService executorService = new ThreadPoolExecutor(0, 1, 27103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()); 27203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private final Callable<Void> cleanupCallable = new Callable<Void>() { 27303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch @Override public Void call() throws Exception { 27403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch synchronized (DiskLruCache.this) { 27503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (journalWriter == null) { 27603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch return null; // closed 27703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 27803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch trimToSize(); 27903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (journalRebuildRequired()) { 28003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch rebuildJournal(); 28103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch redundantOpCount = 0; 28203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 28303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 28403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch return null; 28503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 28603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch }; 28703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 28803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private DiskLruCache(File directory, int appVersion, int valueCount, long maxSize) { 28903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch this.directory = directory; 29003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch this.appVersion = appVersion; 29103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch this.journalFile = new File(directory, JOURNAL_FILE); 29203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch this.journalFileTmp = new File(directory, JOURNAL_FILE_TMP); 29303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch this.valueCount = valueCount; 29403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch this.maxSize = maxSize; 2952bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch } 2962bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch 2972bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch /** 29803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * Opens the cache in {@code directory}, creating a cache if none exists 29903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * there. 3002bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch * 30103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * @param directory a writable directory 30203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * @param appVersion 30303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * @param valueCount the number of values per cache entry. Must be positive. 30403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * @param maxSize the maximum number of bytes this cache should use to store 30503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * @throws IOException if reading or writing the cache directory fails 3062bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch */ 30703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize) 30803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch throws IOException { 30903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (maxSize <= 0) { 31003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch throw new IllegalArgumentException("maxSize <= 0"); 31103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 31203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (valueCount <= 0) { 31303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch throw new IllegalArgumentException("valueCount <= 0"); 31403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 31503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 31603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch // prefer to pick up where we left off 31703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch DiskLruCache cache = new DiskLruCache(directory, appVersion, valueCount, maxSize); 31803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (cache.journalFile.exists()) { 31903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch try { 32003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch cache.readJournal(); 32103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch cache.processJournal(); 32203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch cache.journalWriter = new BufferedWriter(new FileWriter(cache.journalFile, true), 32303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch IO_BUFFER_SIZE); 32403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch return cache; 32503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } catch (IOException journalIsCorrupt) { 32603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch// System.logW("DiskLruCache " + directory + " is corrupt: " 32703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch// + journalIsCorrupt.getMessage() + ", removing"); 32803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch cache.delete(); 32903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 33003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 33103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 33203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch // create a new empty cache 33303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch directory.mkdirs(); 33403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch cache = new DiskLruCache(directory, appVersion, valueCount, maxSize); 33503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch cache.rebuildJournal(); 33603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch return cache; 33703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 33803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 33903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private void readJournal() throws IOException { 34003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch InputStream in = new BufferedInputStream(new FileInputStream(journalFile), IO_BUFFER_SIZE); 34103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch try { 34203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch String magic = readAsciiLine(in); 34303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch String version = readAsciiLine(in); 34403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch String appVersionString = readAsciiLine(in); 34503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch String valueCountString = readAsciiLine(in); 34603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch String blank = readAsciiLine(in); 34703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (!MAGIC.equals(magic) 34803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch || !VERSION_1.equals(version) 34903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch || !Integer.toString(appVersion).equals(appVersionString) 35003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch || !Integer.toString(valueCount).equals(valueCountString) 35103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch || !"".equals(blank)) { 35203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch throw new IOException("unexpected journal header: [" 35303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch + magic + ", " + version + ", " + valueCountString + ", " + blank + "]"); 35403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 35503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 35603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch while (true) { 3572bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch try { 35803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch readJournalLine(readAsciiLine(in)); 35903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } catch (EOFException endOfJournal) { 36003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch break; 3612bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch } 3622bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch } 36303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } finally { 36403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch closeQuietly(in); 3652bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch } 3662bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch } 3672bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch 36803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private void readJournalLine(String line) throws IOException { 36903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch String[] parts = line.split(" "); 37003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (parts.length < 2) { 37103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch throw new IOException("unexpected journal line: " + line); 37203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 37303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 37403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch String key = parts[1]; 37503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (parts[0].equals(REMOVE) && parts.length == 2) { 37603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch lruEntries.remove(key); 37703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch return; 37803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 37903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 38003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch Entry entry = lruEntries.get(key); 38103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (entry == null) { 38203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch entry = new Entry(key); 38303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch lruEntries.put(key, entry); 38403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 38503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 38603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (parts[0].equals(CLEAN) && parts.length == 2 + valueCount) { 38703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch entry.readable = true; 38803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch entry.currentEditor = null; 38903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch entry.setLengths(copyOfRange(parts, 2, parts.length)); 39003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } else if (parts[0].equals(DIRTY) && parts.length == 2) { 39103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch entry.currentEditor = new Editor(entry); 39203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } else if (parts[0].equals(READ) && parts.length == 2) { 39303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch // this work was already done by calling lruEntries.get() 39403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } else { 39503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch throw new IOException("unexpected journal line: " + line); 39603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 3972bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch } 3982bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch 3992bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch /** 40003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * Computes the initial size and collects garbage as a part of opening the 40103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * cache. Dirty entries are assumed to be inconsistent and will be deleted. 4022bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch */ 40303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private void processJournal() throws IOException { 40403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch deleteIfExists(journalFileTmp); 40503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch for (Iterator<Entry> i = lruEntries.values().iterator(); i.hasNext(); ) { 40603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch Entry entry = i.next(); 40703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (entry.currentEditor == null) { 40803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch for (int t = 0; t < valueCount; t++) { 40903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch size += entry.lengths[t]; 41003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 41103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } else { 41203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch entry.currentEditor = null; 41303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch for (int t = 0; t < valueCount; t++) { 41403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch deleteIfExists(entry.getCleanFile(t)); 41503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch deleteIfExists(entry.getDirtyFile(t)); 41603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 41703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch i.remove(); 4182bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch } 4192bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch } 4202bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch } 4212bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch 4222bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch /** 42303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * Creates a new journal that omits redundant information. This replaces the 42403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * current journal if it exists. 4252bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch */ 42603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private synchronized void rebuildJournal() throws IOException { 42703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (journalWriter != null) { 42803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch journalWriter.close(); 42903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 43003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 43103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch Writer writer = new BufferedWriter(new FileWriter(journalFileTmp), IO_BUFFER_SIZE); 43203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch writer.write(MAGIC); 43303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch writer.write("\n"); 43403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch writer.write(VERSION_1); 43503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch writer.write("\n"); 43603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch writer.write(Integer.toString(appVersion)); 43703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch writer.write("\n"); 43803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch writer.write(Integer.toString(valueCount)); 43903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch writer.write("\n"); 44003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch writer.write("\n"); 44103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 44203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch for (Entry entry : lruEntries.values()) { 44303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (entry.currentEditor != null) { 44403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch writer.write(DIRTY + ' ' + entry.key + '\n'); 4452bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch } else { 44603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch writer.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n'); 4472bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch } 44803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 44903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 45003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch writer.close(); 45103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch journalFileTmp.renameTo(journalFile); 45203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch journalWriter = new BufferedWriter(new FileWriter(journalFile, true), IO_BUFFER_SIZE); 45303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 45403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 45503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private static void deleteIfExists(File file) throws IOException { 45603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch// try { 45703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch// Libcore.os.remove(file.getPath()); 45803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch// } catch (ErrnoException errnoException) { 45903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch// if (errnoException.errno != OsConstants.ENOENT) { 46003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch// throw errnoException.rethrowAsIOException(); 46103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch// } 46203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch// } 46303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (file.exists() && !file.delete()) { 46403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch throw new IOException(); 4652bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch } 4662bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch } 4672bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch 4682bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch /** 46903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * Returns a snapshot of the entry named {@code key}, or null if it doesn't 47003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * exist is not currently readable. If a value is returned, it is moved to 47103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * the head of the LRU queue. 4722bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch */ 47303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch public synchronized Snapshot get(String key) throws IOException { 47403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch checkNotClosed(); 47503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch validateKey(key); 47603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch Entry entry = lruEntries.get(key); 47703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (entry == null) { 47803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch return null; 47903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 48003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 48103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (!entry.readable) { 48203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch return null; 48303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 48403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 48503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch /* 48603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * Open all streams eagerly to guarantee that we see a single published 48703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * snapshot. If we opened streams lazily then the streams could come 48803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * from different edits. 48903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch */ 49003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch InputStream[] ins = new InputStream[valueCount]; 49103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch try { 49203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch for (int i = 0; i < valueCount; i++) { 49303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch ins[i] = new FileInputStream(entry.getCleanFile(i)); 49403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 49503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } catch (FileNotFoundException e) { 49603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch // a file must have been deleted manually! 49703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch return null; 4982bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch } 4992bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch 50003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch redundantOpCount++; 50103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch journalWriter.append(READ + ' ' + key + '\n'); 50203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (journalRebuildRequired()) { 50303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch executorService.submit(cleanupCallable); 5042bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch } 50503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 50603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch return new Snapshot(key, entry.sequenceNumber, ins); 5072bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch } 5082bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch 5092bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch /** 51003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * Returns an editor for the entry named {@code key}, or null if another 51103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * edit is in progress. 5122bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch */ 51303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch public Editor edit(String key) throws IOException { 51403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch return edit(key, ANY_SEQUENCE_NUMBER); 51503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 51603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 51703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private synchronized Editor edit(String key, long expectedSequenceNumber) throws IOException { 51803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch checkNotClosed(); 51903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch validateKey(key); 52003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch Entry entry = lruEntries.get(key); 52103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (expectedSequenceNumber != ANY_SEQUENCE_NUMBER 52203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch && (entry == null || entry.sequenceNumber != expectedSequenceNumber)) { 52303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch return null; // snapshot is stale 52403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 52503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (entry == null) { 52603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch entry = new Entry(key); 52703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch lruEntries.put(key, entry); 52803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } else if (entry.currentEditor != null) { 52903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch return null; // another edit is in progress 53003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 53103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 53203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch Editor editor = new Editor(entry); 53303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch entry.currentEditor = editor; 53403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 53503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch // flush the journal before creating files to prevent file leaks 53603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch journalWriter.write(DIRTY + ' ' + key + '\n'); 53703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch journalWriter.flush(); 53803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch return editor; 5392bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch } 5402bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch 5412bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch /** 54203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * Returns the directory where this cache stores its data. 5432bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch */ 54403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch public File getDirectory() { 54503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch return directory; 5462bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch } 5472bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch 5482bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch /** 54903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * Returns the maximum number of bytes that this cache should use to store 55003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * its data. 5512bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch */ 55203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch public long maxSize() { 55303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch return maxSize; 55403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 55503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 55603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch /** 55703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * Returns the number of bytes currently being used to store the values in 55803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * this cache. This may be greater than the max size if a background 55903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * deletion is pending. 56003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch */ 56103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch public synchronized long size() { 56203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch return size; 56303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 56403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 56503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private synchronized void completeEdit(Editor editor, boolean success) throws IOException { 56603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch Entry entry = editor.entry; 56703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (entry.currentEditor != editor) { 56803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch throw new IllegalStateException(); 56903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 57003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 57103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch // if this edit is creating the entry for the first time, every index must have a value 57203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (success && !entry.readable) { 57303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch for (int i = 0; i < valueCount; i++) { 57403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (!entry.getDirtyFile(i).exists()) { 57503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch editor.abort(); 57603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch throw new IllegalStateException("edit didn't create file " + i); 57703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 57803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 57903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 58003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 58103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch for (int i = 0; i < valueCount; i++) { 58203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch File dirty = entry.getDirtyFile(i); 58303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (success) { 58403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (dirty.exists()) { 58503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch File clean = entry.getCleanFile(i); 58603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch dirty.renameTo(clean); 58703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch long oldLength = entry.lengths[i]; 58803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch long newLength = clean.length(); 58903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch entry.lengths[i] = newLength; 59003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch size = size - oldLength + newLength; 59103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 59203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } else { 59303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch deleteIfExists(dirty); 59403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 59503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 59603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 59703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch redundantOpCount++; 59803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch entry.currentEditor = null; 59903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (entry.readable | success) { 60003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch entry.readable = true; 60103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch journalWriter.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n'); 60203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (success) { 60303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch entry.sequenceNumber = nextSequenceNumber++; 60403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 60503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } else { 60603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch lruEntries.remove(entry.key); 60703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch journalWriter.write(REMOVE + ' ' + entry.key + '\n'); 60803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 60903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 61003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (size > maxSize || journalRebuildRequired()) { 61103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch executorService.submit(cleanupCallable); 6122bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch } 6132bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch } 6142bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch 6152bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch /** 61603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * We only rebuild the journal when it will halve the size of the journal 61703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * and eliminate at least 2000 ops. 61803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch */ 61903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private boolean journalRebuildRequired() { 62003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch final int REDUNDANT_OP_COMPACT_THRESHOLD = 2000; 62103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch return redundantOpCount >= REDUNDANT_OP_COMPACT_THRESHOLD 62203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch && redundantOpCount >= lruEntries.size(); 62303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 62403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 62503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch /** 62603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * Drops the entry for {@code key} if it exists and can be removed. Entries 62703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * actively being edited cannot be removed. 6282bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch * 62903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * @return true if an entry was removed. 6302bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch */ 63103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch public synchronized boolean remove(String key) throws IOException { 63203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch checkNotClosed(); 63303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch validateKey(key); 63403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch Entry entry = lruEntries.get(key); 63503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (entry == null || entry.currentEditor != null) { 63603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch return false; 63703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 63803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 63903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch for (int i = 0; i < valueCount; i++) { 64003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch File file = entry.getCleanFile(i); 64103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (!file.delete()) { 64203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch throw new IOException("failed to delete " + file); 64303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 64403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch size -= entry.lengths[i]; 64503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch entry.lengths[i] = 0; 64603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 6472bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch 64803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch redundantOpCount++; 64903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch journalWriter.append(REMOVE + ' ' + key + '\n'); 65003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch lruEntries.remove(key); 6512bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch 65203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (journalRebuildRequired()) { 65303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch executorService.submit(cleanupCallable); 65403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 65503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 65603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch return true; 6572bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch } 6582bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch 6592bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch /** 66003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * Returns true if this cache has been closed. 6612bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch */ 66203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch public boolean isClosed() { 66303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch return journalWriter == null; 66403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 66503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 66603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private void checkNotClosed() { 66703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (journalWriter == null) { 66803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch throw new IllegalStateException("cache is closed"); 6692bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch } 67003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 6712bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch 67203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch /** 67303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * Force buffered operations to the filesystem. 67403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch */ 67503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch public synchronized void flush() throws IOException { 67603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch checkNotClosed(); 67703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch trimToSize(); 67803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch journalWriter.flush(); 6792bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch } 6802bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch 6812bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch /** 68203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * Closes this cache. Stored values will remain on the filesystem. 68303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch */ 68403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch public synchronized void close() throws IOException { 68503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (journalWriter == null) { 68603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch return; // already closed 68703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 68803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch for (Entry entry : new ArrayList<Entry>(lruEntries.values())) { 68903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (entry.currentEditor != null) { 69003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch entry.currentEditor.abort(); 69103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 69203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 69303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch trimToSize(); 69403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch journalWriter.close(); 69503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch journalWriter = null; 69603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 69703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 69803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private void trimToSize() throws IOException { 69903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch while (size > maxSize) { 70003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch// Map.Entry<String, Entry> toEvict = lruEntries.eldest(); 70103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch final Map.Entry<String, Entry> toEvict = lruEntries.entrySet().iterator().next(); 70203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch remove(toEvict.getKey()); 70303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 70403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 70503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 70603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch /** 70703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * Closes the cache and deletes all of its stored values. This will delete 70803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * all files in the cache directory including files that weren't created by 70903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * the cache. 7102bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch */ 71103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch public void delete() throws IOException { 71203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch close(); 71303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch deleteContents(directory); 71403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 71503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 71603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private void validateKey(String key) { 71703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (key.contains(" ") || key.contains("\n") || key.contains("\r")) { 71803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch throw new IllegalArgumentException( 71903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch "keys must not contain spaces or newlines: \"" + key + "\""); 72003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 72103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 72203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 72303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private static String inputStreamToString(InputStream in) throws IOException { 72403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch return readFully(new InputStreamReader(in, UTF_8)); 7252bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch } 7262bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch 7272bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch /** 72803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * A snapshot of the values for an entry. 7292bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch */ 73003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch public final class Snapshot implements Closeable { 73103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private final String key; 73203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private final long sequenceNumber; 73303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private final InputStream[] ins; 73403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 73503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private Snapshot(String key, long sequenceNumber, InputStream[] ins) { 73603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch this.key = key; 73703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch this.sequenceNumber = sequenceNumber; 73803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch this.ins = ins; 73903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 74003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 74103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch /** 74203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * Returns an editor for this snapshot's entry, or null if either the 74303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * entry has changed since this snapshot was created or if another edit 74403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * is in progress. 74503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch */ 74603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch public Editor edit() throws IOException { 74703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch return DiskLruCache.this.edit(key, sequenceNumber); 74803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 74903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 75003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch /** 75103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * Returns the unbuffered stream with the value for {@code index}. 75203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch */ 75303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch public InputStream getInputStream(int index) { 75403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch return ins[index]; 75503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 75603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 75703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch /** 75803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * Returns the string value for {@code index}. 75903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch */ 76003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch public String getString(int index) throws IOException { 76103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch return inputStreamToString(getInputStream(index)); 76203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 76303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 76403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch @Override public void close() { 76503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch for (InputStream in : ins) { 76603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch closeQuietly(in); 76703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 76803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 7692bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch } 7702bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch 7712bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch /** 77203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * Edits the values for an entry. 7732bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch */ 77403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch public final class Editor { 77503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private final Entry entry; 77603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private boolean hasErrors; 7772bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch 77803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private Editor(Entry entry) { 77903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch this.entry = entry; 78003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 78103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 78203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch /** 78303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * Returns an unbuffered input stream to read the last committed value, 78403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * or null if no value has been committed. 78503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch */ 78603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch public InputStream newInputStream(int index) throws IOException { 78703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch synchronized (DiskLruCache.this) { 78803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (entry.currentEditor != this) { 78903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch throw new IllegalStateException(); 79003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 79103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (!entry.readable) { 79203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch return null; 79303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 79403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch return new FileInputStream(entry.getCleanFile(index)); 79503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 79603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 79703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 79803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch /** 79903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * Returns the last committed value as a string, or null if no value 80003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * has been committed. 80103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch */ 80203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch public String getString(int index) throws IOException { 80303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch InputStream in = newInputStream(index); 80403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch return in != null ? inputStreamToString(in) : null; 80503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 80603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 80703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch /** 80803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * Returns a new unbuffered output stream to write the value at 80903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * {@code index}. If the underlying output stream encounters errors 81003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * when writing to the filesystem, this edit will be aborted when 81103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * {@link #commit} is called. The returned output stream does not throw 81203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * IOExceptions. 81303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch */ 81403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch public OutputStream newOutputStream(int index) throws IOException { 81503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch synchronized (DiskLruCache.this) { 81603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (entry.currentEditor != this) { 81703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch throw new IllegalStateException(); 81803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 81903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch return new FaultHidingOutputStream(new FileOutputStream(entry.getDirtyFile(index))); 82003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 82103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 82203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 82303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch /** 82403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * Sets the value at {@code index} to {@code value}. 82503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch */ 82603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch public void set(int index, String value) throws IOException { 82703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch Writer writer = null; 82803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch try { 82903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch writer = new OutputStreamWriter(newOutputStream(index), UTF_8); 83003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch writer.write(value); 83103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } finally { 83203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch closeQuietly(writer); 83303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 83403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 83503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 83603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch /** 83703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * Commits this edit so it is visible to readers. This releases the 83803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * edit lock so another edit may be started on the same key. 83903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch */ 84003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch public void commit() throws IOException { 84103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (hasErrors) { 84203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch completeEdit(this, false); 84303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch remove(entry.key); // the previous entry is stale 84403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } else { 84503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch completeEdit(this, true); 84603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 84703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 84803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 84903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch /** 85003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * Aborts this edit. This releases the edit lock so another edit may be 85103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * started on the same key. 85203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch */ 85303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch public void abort() throws IOException { 85403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch completeEdit(this, false); 85503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 85603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 85703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private class FaultHidingOutputStream extends FilterOutputStream { 85803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private FaultHidingOutputStream(OutputStream out) { 85903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch super(out); 86003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 86103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 86203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch @Override public void write(int oneByte) { 86303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch try { 86403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch out.write(oneByte); 86503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } catch (IOException e) { 86603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch hasErrors = true; 86703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 86803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 86903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 87003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch @Override public void write(byte[] buffer, int offset, int length) { 87103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch try { 87203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch out.write(buffer, offset, length); 87303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } catch (IOException e) { 87403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch hasErrors = true; 87503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 87603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 87703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 87803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch @Override public void close() { 87903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch try { 88003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch out.close(); 88103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } catch (IOException e) { 88203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch hasErrors = true; 88303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 88403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 88503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 88603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch @Override public void flush() { 88703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch try { 88803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch out.flush(); 88903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } catch (IOException e) { 89003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch hasErrors = true; 89103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 8922bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch } 8932bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch } 8942bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch } 89503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 89603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private final class Entry { 89703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private final String key; 89803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 89903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch /** Lengths of this entry's files. */ 90003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private final long[] lengths; 90103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 90203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch /** True if this entry has ever been published */ 90303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private boolean readable; 90403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 90503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch /** The ongoing edit or null if this entry is not being edited. */ 90603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private Editor currentEditor; 90703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 90803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch /** The sequence number of the most recently committed edit to this entry. */ 90903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private long sequenceNumber; 91003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 91103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private Entry(String key) { 91203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch this.key = key; 91303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch this.lengths = new long[valueCount]; 91403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 91503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 91603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch public String getLengths() throws IOException { 91703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch StringBuilder result = new StringBuilder(); 91803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch for (long size : lengths) { 91903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch result.append(' ').append(size); 92003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 92103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch return result.toString(); 92203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 92303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 92403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch /** 92503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch * Set lengths using decimal numbers like "10123". 92603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch */ 92703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private void setLengths(String[] strings) throws IOException { 92803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch if (strings.length != valueCount) { 92903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch throw invalidLengths(strings); 93003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 93103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 93203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch try { 93303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch for (int i = 0; i < strings.length; i++) { 93403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch lengths[i] = Long.parseLong(strings[i]); 93503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 93603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } catch (NumberFormatException e) { 93703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch throw invalidLengths(strings); 93803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 93903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 94003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 94103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch private IOException invalidLengths(String[] strings) throws IOException { 94203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch throw new IOException("unexpected journal line: " + Arrays.toString(strings)); 94303ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 94403ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 94503ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch public File getCleanFile(int i) { 94603ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch return new File(directory, key + "." + i); 94703ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 94803ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch 94903ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch public File getDirtyFile(int i) { 95003ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch return new File(directory, key + "." + i + ".tmp"); 95103ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 95203ceb37f79e31626aaac5318b3143a2cfafaea42Adam Koch } 9532bab0137e0af92c374d3efb2ed0e1d894981e015Adam Koch} 954