1c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath/*
2c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Copyright (C) 2011 The Android Open Source Project
3c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *
4c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Licensed under the Apache License, Version 2.0 (the "License");
5c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * you may not use this file except in compliance with the License.
6c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * You may obtain a copy of the License at
7c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *
8c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *      http://www.apache.org/licenses/LICENSE-2.0
9c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *
10c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Unless required by applicable law or agreed to in writing, software
11c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * distributed under the License is distributed on an "AS IS" BASIS,
12c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * See the License for the specific language governing permissions and
14c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * limitations under the License.
15c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */
16c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
172231db3e6bb54447a9b14cf004a6cb03c373651cjwilsonpackage com.squareup.okhttp.internal;
18c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
19a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerimport com.squareup.okhttp.internal.io.FileSystem;
20c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.Closeable;
21c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.EOFException;
22c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.File;
23c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.FileNotFoundException;
24c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.io.IOException;
25e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.ArrayList;
26e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.Arrays;
27c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.Iterator;
28c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.LinkedHashMap;
29e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.NoSuchElementException;
30e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.concurrent.Executor;
31c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.concurrent.LinkedBlockingQueue;
32c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.concurrent.ThreadPoolExecutor;
33c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.concurrent.TimeUnit;
34faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamathimport java.util.regex.Matcher;
35faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamathimport java.util.regex.Pattern;
36e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.Buffer;
373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.BufferedSink;
383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.BufferedSource;
393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.Okio;
40e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.Sink;
41e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.Source;
42e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.Timeout;
4354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
44c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath/**
45c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * A cache that uses a bounded amount of space on a filesystem. Each cache
46faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * entry has a string key and a fixed number of values. Each key must match
47faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * the regex <strong>[a-z0-9_-]{1,64}</strong>. Values are byte sequences,
48faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * accessible as streams or files. Each value must be between {@code 0} and
49faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath * {@code Integer.MAX_VALUE} bytes in length.
50c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *
51c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * <p>The cache stores its data in a directory on the filesystem. This
52c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * directory must be exclusive to the cache; the cache may delete or overwrite
53c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * files from its directory. It is an error for multiple processes to use the
54c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * same cache directory at the same time.
55c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *
56c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * <p>This cache limits the number of bytes that it will store on the
57c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * filesystem. When the number of stored bytes exceeds the limit, the cache will
58c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * remove entries in the background until the limit is satisfied. The limit is
59c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * not strict: the cache may temporarily exceed it while waiting for files to be
60c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * deleted. The limit does not include filesystem overhead or the cache
61c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * journal so space-sensitive applications should set a conservative limit.
62c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *
63c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * <p>Clients call {@link #edit} to create or update the values of an entry. An
64c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * entry may have only one editor at one time; if a value is not available to be
65c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * edited then {@link #edit} will return null.
66c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * <ul>
67faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath *     <li>When an entry is being <strong>created</strong> it is necessary to
68faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath *         supply a full set of values; the empty value should be used as a
69faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath *         placeholder if necessary.
70faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath *     <li>When an entry is being <strong>edited</strong>, it is not necessary
71faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath *         to supply data for every value; values default to their previous
72faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath *         value.
73c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * </ul>
74c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Every {@link #edit} call must be matched by a call to {@link Editor#commit}
75c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * or {@link Editor#abort}. Committing is atomic: a read observes the full set
76c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * of values as they were before or after the commit, but never a mix of values.
77c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *
78c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * <p>Clients call {@link #get} to read a snapshot of an entry. The read will
79c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * observe the value at the time that {@link #get} was called. Updates and
80c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * removals after the call do not impact ongoing reads.
81c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath *
82c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * <p>This class is tolerant of some I/O errors. If files are missing from the
83c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * filesystem, the corresponding entries will be dropped from the cache. If
84c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * an error occurs while writing a cache value, the edit will fail silently.
85c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Callers should handle other problems by catching {@code IOException} and
86c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * responding appropriately.
87c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */
88c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathpublic final class DiskLruCache implements Closeable {
8954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  static final String JOURNAL_FILE = "journal";
90faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  static final String JOURNAL_FILE_TEMP = "journal.tmp";
91faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  static final String JOURNAL_FILE_BACKUP = "journal.bkp";
9254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  static final String MAGIC = "libcore.io.DiskLruCache";
9354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  static final String VERSION_1 = "1";
9454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  static final long ANY_SEQUENCE_NUMBER = -1;
95e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  static final Pattern LEGAL_KEY_PATTERN = Pattern.compile("[a-z0-9_-]{1,120}");
9654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private static final String CLEAN = "CLEAN";
9754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private static final String DIRTY = "DIRTY";
9854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private static final String REMOVE = "REMOVE";
9954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private static final String READ = "READ";
10054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
101faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    /*
102faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath     * This cache uses a journal file named "journal". A typical journal file
103faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath     * looks like this:
104faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath     *     libcore.io.DiskLruCache
105faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath     *     1
106faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath     *     100
107faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath     *     2
108faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath     *
109faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath     *     CLEAN 3400330d1dfc7f3f7f4b8d4d803dfcf6 832 21054
110faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath     *     DIRTY 335c4c6028171cfddfbaae1a9c313c52
111faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath     *     CLEAN 335c4c6028171cfddfbaae1a9c313c52 3934 2342
112faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath     *     REMOVE 335c4c6028171cfddfbaae1a9c313c52
113faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath     *     DIRTY 1ab96a171faeeee38496d8b330771a7a
114faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath     *     CLEAN 1ab96a171faeeee38496d8b330771a7a 1600 234
115faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath     *     READ 335c4c6028171cfddfbaae1a9c313c52
116faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath     *     READ 3400330d1dfc7f3f7f4b8d4d803dfcf6
117faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath     *
118faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath     * The first five lines of the journal form its header. They are the
119faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath     * constant string "libcore.io.DiskLruCache", the disk cache's version,
120faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath     * the application's version, the value count, and a blank line.
121faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath     *
122faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath     * Each of the subsequent lines in the file is a record of the state of a
123faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath     * cache entry. Each line contains space-separated values: a state, a key,
124faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath     * and optional state-specific values.
125faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath     *   o DIRTY lines track that an entry is actively being created or updated.
126faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath     *     Every successful DIRTY action should be followed by a CLEAN or REMOVE
127faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath     *     action. DIRTY lines without a matching CLEAN or REMOVE indicate that
128faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath     *     temporary files may need to be deleted.
129faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath     *   o CLEAN lines track a cache entry that has been successfully published
130faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath     *     and may be read. A publish line is followed by the lengths of each of
131faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath     *     its values.
132faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath     *   o READ lines track accesses for LRU.
133faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath     *   o REMOVE lines track entries that have been deleted.
134faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath     *
135faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath     * The journal file is appended to as cache operations occur. The journal may
136faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath     * occasionally be compacted by dropping redundant lines. A temporary file named
137faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath     * "journal.tmp" will be used during compaction; that file should be deleted if
138faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath     * it exists when the cache is opened.
139faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath     */
14054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
141a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller  private final FileSystem fileSystem;
14254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private final File directory;
14354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private final File journalFile;
14454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private final File journalFileTmp;
145faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  private final File journalFileBackup;
14654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private final int appVersion;
147faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  private long maxSize;
14854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private final int valueCount;
14954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private long size = 0;
1503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private BufferedSink journalWriter;
151e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private final LinkedHashMap<String, Entry> lruEntries = new LinkedHashMap<>(0, 0.75f, true);
15254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private int redundantOpCount;
153a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller  private boolean hasJournalErrors;
15454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
155e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  // Must be read and written when synchronized on 'this'.
156e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private boolean initialized;
157e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private boolean closed;
158e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
15954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
16054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * To differentiate between old and current snapshots, each entry is given
16154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * a sequence number each time an edit is committed. A snapshot is stale if
16254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * its sequence number is not equal to its entry's sequence number.
16354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
16454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private long nextSequenceNumber = 0;
16554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
166e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /** Used to run 'cleanupRunnable' for journal rebuilds. */
167e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private final Executor executor;
1683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller  private final Runnable cleanupRunnable = new Runnable() {
1693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    public void run() {
17054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      synchronized (DiskLruCache.this) {
171e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        if (!initialized | closed) {
172e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          return; // Nothing to do
173c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
1743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        try {
1753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          trimToSize();
1763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          if (journalRebuildRequired()) {
1773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            rebuildJournal();
1783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller            redundantOpCount = 0;
1793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          }
1803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller        } catch (IOException e) {
1813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller          throw new RuntimeException(e);
182c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
18354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
18454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
18554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  };
18654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
187a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller  DiskLruCache(FileSystem fileSystem, File directory, int appVersion, int valueCount, long maxSize,
188a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      Executor executor) {
189a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    this.fileSystem = fileSystem;
19054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    this.directory = directory;
19154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    this.appVersion = appVersion;
19254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    this.journalFile = new File(directory, JOURNAL_FILE);
193faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    this.journalFileTmp = new File(directory, JOURNAL_FILE_TEMP);
194faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    this.journalFileBackup = new File(directory, JOURNAL_FILE_BACKUP);
19554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    this.valueCount = valueCount;
19654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    this.maxSize = maxSize;
197e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    this.executor = executor;
19854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
19954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
20071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller  public synchronized void initialize() throws IOException {
201e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assert Thread.holdsLock(this);
202e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
203e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    if (initialized) {
204e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      return; // Already initialized.
20554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
206c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
207faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    // If a bkp file exists, use it instead.
208a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    if (fileSystem.exists(journalFileBackup)) {
209faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      // If journal file also exists just delete backup file.
210a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      if (fileSystem.exists(journalFile)) {
211a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        fileSystem.delete(journalFileBackup);
212faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      } else {
213a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        fileSystem.rename(journalFileBackup, journalFile);
214faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      }
215faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    }
216faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
217faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    // Prefer to pick up where we left off.
218a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    if (fileSystem.exists(journalFile)) {
21954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      try {
220e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        readJournal();
221e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        processJournal();
222e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        initialized = true;
223e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        return;
22454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      } catch (IOException journalIsCorrupt) {
225faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        Platform.get().logW("DiskLruCache " + directory + " is corrupt: "
226faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath            + journalIsCorrupt.getMessage() + ", removing");
227e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        delete();
228e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        closed = false;
22954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
230c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
231c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
232e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    rebuildJournal();
233e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
234e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    initialized = true;
235e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
236e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
237e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /**
238e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * Create a cache which will reside in {@code directory}. This cache is lazily initialized on
239e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * first access and will be created if it does not exist.
240e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   *
241e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * @param directory a writable directory
242e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * @param valueCount the number of values per cache entry. Must be positive.
243e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * @param maxSize the maximum number of bytes this cache should use to store
244e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   */
245a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller  public static DiskLruCache create(FileSystem fileSystem, File directory, int appVersion,
246a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      int valueCount, long maxSize) {
247e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    if (maxSize <= 0) {
248e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      throw new IllegalArgumentException("maxSize <= 0");
249e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
250e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    if (valueCount <= 0) {
251e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      throw new IllegalArgumentException("valueCount <= 0");
252e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
253e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
254e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Use a single background thread to evict entries.
255e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Executor executor = new ThreadPoolExecutor(0, 1, 60L, TimeUnit.SECONDS,
256e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        new LinkedBlockingQueue<Runnable>(), Util.threadFactory("OkHttp DiskLruCache", true));
257e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
258a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    return new DiskLruCache(fileSystem, directory, appVersion, valueCount, maxSize, executor);
25954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
26054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
26154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void readJournal() throws IOException {
262a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    BufferedSource source = Okio.buffer(fileSystem.source(journalFile));
26354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    try {
264c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller      String magic = source.readUtf8LineStrict();
265c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller      String version = source.readUtf8LineStrict();
266c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller      String appVersionString = source.readUtf8LineStrict();
267c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller      String valueCountString = source.readUtf8LineStrict();
268c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller      String blank = source.readUtf8LineStrict();
269faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      if (!MAGIC.equals(magic)
270faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath          || !VERSION_1.equals(version)
271faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath          || !Integer.toString(appVersion).equals(appVersionString)
272faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath          || !Integer.toString(valueCount).equals(valueCountString)
273faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath          || !"".equals(blank)) {
274faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        throw new IOException("unexpected journal header: [" + magic + ", " + version + ", "
275faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath            + valueCountString + ", " + blank + "]");
27654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
27754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
278faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      int lineCount = 0;
27954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      while (true) {
280c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        try {
281c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller          readJournalLine(source.readUtf8LineStrict());
282faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath          lineCount++;
28354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        } catch (EOFException endOfJournal) {
28454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          break;
285c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
28654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
287faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      redundantOpCount = lineCount - lruEntries.size();
288e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
289e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      // If we ended on a truncated line, rebuild the journal before appending to it.
290e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      if (!source.exhausted()) {
291e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        rebuildJournal();
292e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      } else {
293a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        journalWriter = newJournalWriter();
294e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
29554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } finally {
2963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller      Util.closeQuietly(source);
297c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
29854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
299c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
300a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller  private BufferedSink newJournalWriter() throws FileNotFoundException {
301a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    Sink fileSink = fileSystem.appendingSink(journalFile);
302a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    Sink faultHidingSink = new FaultHidingSink(fileSink) {
303a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      @Override protected void onException(IOException e) {
304a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        assert (Thread.holdsLock(DiskLruCache.this));
305a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        hasJournalErrors = true;
306a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      }
307a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    };
308a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    return Okio.buffer(faultHidingSink);
309a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller  }
310a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller
31154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void readJournalLine(String line) throws IOException {
312faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    int firstSpace = line.indexOf(' ');
313faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    if (firstSpace == -1) {
31454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      throw new IOException("unexpected journal line: " + line);
315c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
316c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
317faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    int keyBegin = firstSpace + 1;
318faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    int secondSpace = line.indexOf(' ', keyBegin);
319faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    final String key;
320faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    if (secondSpace == -1) {
321faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      key = line.substring(keyBegin);
322faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      if (firstSpace == REMOVE.length() && line.startsWith(REMOVE)) {
323faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        lruEntries.remove(key);
324faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        return;
325faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      }
326faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    } else {
327faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      key = line.substring(keyBegin, secondSpace);
328c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
329c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
33054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    Entry entry = lruEntries.get(key);
33154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (entry == null) {
33254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      entry = new Entry(key);
33354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      lruEntries.put(key, entry);
33454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
335c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
336faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    if (secondSpace != -1 && firstSpace == CLEAN.length() && line.startsWith(CLEAN)) {
337faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      String[] parts = line.substring(secondSpace + 1).split(" ");
33854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      entry.readable = true;
33954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      entry.currentEditor = null;
340faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      entry.setLengths(parts);
341faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    } else if (secondSpace == -1 && firstSpace == DIRTY.length() && line.startsWith(DIRTY)) {
34254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      entry.currentEditor = new Editor(entry);
343faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    } else if (secondSpace == -1 && firstSpace == READ.length() && line.startsWith(READ)) {
344faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      // This work was already done by calling lruEntries.get().
34554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } else {
34654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      throw new IOException("unexpected journal line: " + line);
34754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
34854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
34954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
35054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
35154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Computes the initial size and collects garbage as a part of opening the
35254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * cache. Dirty entries are assumed to be inconsistent and will be deleted.
35354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
35454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void processJournal() throws IOException {
355a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    fileSystem.delete(journalFileTmp);
35654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    for (Iterator<Entry> i = lruEntries.values().iterator(); i.hasNext(); ) {
35754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      Entry entry = i.next();
35854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      if (entry.currentEditor == null) {
35954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        for (int t = 0; t < valueCount; t++) {
36054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          size += entry.lengths[t];
36154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        }
36254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      } else {
36354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        entry.currentEditor = null;
36454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        for (int t = 0; t < valueCount; t++) {
365a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller          fileSystem.delete(entry.cleanFiles[t]);
366a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller          fileSystem.delete(entry.dirtyFiles[t]);
367c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
36854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        i.remove();
36954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
37054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
37154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
37254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
37354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
37454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Creates a new journal that omits redundant information. This replaces the
37554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * current journal if it exists.
37654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
37754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private synchronized void rebuildJournal() throws IOException {
37854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (journalWriter != null) {
37954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      journalWriter.close();
380c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
381c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
382a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    BufferedSink writer = Okio.buffer(fileSystem.sink(journalFileTmp));
383faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    try {
384e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      writer.writeUtf8(MAGIC).writeByte('\n');
385e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      writer.writeUtf8(VERSION_1).writeByte('\n');
386a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      writer.writeDecimalLong(appVersion).writeByte('\n');
387a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      writer.writeDecimalLong(valueCount).writeByte('\n');
388e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      writer.writeByte('\n');
389faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
390faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      for (Entry entry : lruEntries.values()) {
391faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        if (entry.currentEditor != null) {
392e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          writer.writeUtf8(DIRTY).writeByte(' ');
393e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          writer.writeUtf8(entry.key);
394e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          writer.writeByte('\n');
395faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        } else {
396e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          writer.writeUtf8(CLEAN).writeByte(' ');
397e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          writer.writeUtf8(entry.key);
398e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          entry.writeLengths(writer);
399e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          writer.writeByte('\n');
400faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        }
40154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
402faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    } finally {
403faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      writer.close();
404faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    }
405faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
406a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    if (fileSystem.exists(journalFile)) {
407a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      fileSystem.rename(journalFile, journalFileBackup);
408c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
409a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    fileSystem.rename(journalFileTmp, journalFile);
410a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    fileSystem.delete(journalFileBackup);
411c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
412a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    journalWriter = newJournalWriter();
413a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    hasJournalErrors = false;
41454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
41554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
41654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
41754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Returns a snapshot of the entry named {@code key}, or null if it doesn't
41854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * exist is not currently readable. If a value is returned, it is moved to
41954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * the head of the LRU queue.
42054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
42154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public synchronized Snapshot get(String key) throws IOException {
422e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    initialize();
423e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
42454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    checkNotClosed();
42554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    validateKey(key);
42654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    Entry entry = lruEntries.get(key);
427e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    if (entry == null || !entry.readable) return null;
428c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
429e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Snapshot snapshot = entry.snapshot();
430e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    if (snapshot == null) return null;
431c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
43254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    redundantOpCount++;
433e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    journalWriter.writeUtf8(READ).writeByte(' ').writeUtf8(key).writeByte('\n');
43454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (journalRebuildRequired()) {
435e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      executor.execute(cleanupRunnable);
43654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
437c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
438e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return snapshot;
43954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
44054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
44154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
44254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Returns an editor for the entry named {@code key}, or null if another
44354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * edit is in progress.
44454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
44554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public Editor edit(String key) throws IOException {
44654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return edit(key, ANY_SEQUENCE_NUMBER);
44754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
44854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
44954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private synchronized Editor edit(String key, long expectedSequenceNumber) throws IOException {
450e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    initialize();
451e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
45254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    checkNotClosed();
45354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    validateKey(key);
45454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    Entry entry = lruEntries.get(key);
45554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (expectedSequenceNumber != ANY_SEQUENCE_NUMBER && (entry == null
45654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        || entry.sequenceNumber != expectedSequenceNumber)) {
457faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      return null; // Snapshot is stale.
45854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
459a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    if (entry != null && entry.currentEditor != null) {
460faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      return null; // Another edit is in progress.
461c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
462c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
463faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    // Flush the journal before creating files to prevent file leaks.
464e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    journalWriter.writeUtf8(DIRTY).writeByte(' ').writeUtf8(key).writeByte('\n');
46554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    journalWriter.flush();
466a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller
467a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    if (hasJournalErrors) {
468a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      return null; // Don't edit; the journal can't be written.
469a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    }
470a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller
471a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    if (entry == null) {
472a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      entry = new Entry(key);
473a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      lruEntries.put(key, entry);
474a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    }
475a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    Editor editor = new Editor(entry);
476a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    entry.currentEditor = editor;
47754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return editor;
47854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
47954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
48054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /** Returns the directory where this cache stores its data. */
48154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public File getDirectory() {
48254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return directory;
48354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
48454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
48554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
48654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Returns the maximum number of bytes that this cache should use to store
48754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * its data.
48854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
489c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller  public synchronized long getMaxSize() {
49054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return maxSize;
49154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
49254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
49354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
494faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath   * Changes the maximum number of bytes the cache can store and queues a job
495faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath   * to trim the existing store, if necessary.
496faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath   */
497faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  public synchronized void setMaxSize(long maxSize) {
498faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    this.maxSize = maxSize;
499e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    if (initialized) {
500e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      executor.execute(cleanupRunnable);
501e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
502faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  }
503faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
504faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  /**
50554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Returns the number of bytes currently being used to store the values in
50654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * this cache. This may be greater than the max size if a background
50754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * deletion is pending.
50854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
509e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  public synchronized long size() throws IOException {
510e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    initialize();
51154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return size;
51254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
51354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
51454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private synchronized void completeEdit(Editor editor, boolean success) throws IOException {
51554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    Entry entry = editor.entry;
51654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (entry.currentEditor != editor) {
51754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      throw new IllegalStateException();
518c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
519c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
520faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    // If this edit is creating the entry for the first time, every index must have a value.
52154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (success && !entry.readable) {
52254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      for (int i = 0; i < valueCount; i++) {
52354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        if (!editor.written[i]) {
52454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          editor.abort();
52554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          throw new IllegalStateException("Newly created entry didn't create value for index " + i);
526c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
527a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        if (!fileSystem.exists(entry.dirtyFiles[i])) {
52854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          editor.abort();
52954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          return;
530c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
53154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
53254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
533c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
53454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    for (int i = 0; i < valueCount; i++) {
535e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      File dirty = entry.dirtyFiles[i];
53654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      if (success) {
537a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        if (fileSystem.exists(dirty)) {
538e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          File clean = entry.cleanFiles[i];
539a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller          fileSystem.rename(dirty, clean);
54054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          long oldLength = entry.lengths[i];
541a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller          long newLength = fileSystem.size(clean);
54254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          entry.lengths[i] = newLength;
54354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          size = size - oldLength + newLength;
54454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        }
54554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      } else {
546a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        fileSystem.delete(dirty);
54754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
548c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
549c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
55054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    redundantOpCount++;
55154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    entry.currentEditor = null;
55254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (entry.readable | success) {
55354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      entry.readable = true;
554e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      journalWriter.writeUtf8(CLEAN).writeByte(' ');
555e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      journalWriter.writeUtf8(entry.key);
556e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      entry.writeLengths(journalWriter);
557e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      journalWriter.writeByte('\n');
55854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      if (success) {
55954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        entry.sequenceNumber = nextSequenceNumber++;
56054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
56154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    } else {
56254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      lruEntries.remove(entry.key);
563e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      journalWriter.writeUtf8(REMOVE).writeByte(' ');
564e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      journalWriter.writeUtf8(entry.key);
565e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      journalWriter.writeByte('\n');
566c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
567faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    journalWriter.flush();
568c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
56954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (size > maxSize || journalRebuildRequired()) {
570e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      executor.execute(cleanupRunnable);
57154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
57254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
57354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
57454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
57554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * We only rebuild the journal when it will halve the size of the journal
57654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * and eliminate at least 2000 ops.
57754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
57854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private boolean journalRebuildRequired() {
57954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    final int redundantOpCompactThreshold = 2000;
5803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    return redundantOpCount >= redundantOpCompactThreshold
581faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        && redundantOpCount >= lruEntries.size();
58254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
58354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
58454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
585e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * Drops the entry for {@code key} if it exists and can be removed. If the
586e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * entry for {@code key} is currently being edited, that edit will complete
587e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * normally but its value will not be stored.
58854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   *
58954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * @return true if an entry was removed.
59054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
59154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public synchronized boolean remove(String key) throws IOException {
592e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    initialize();
593e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
59454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    checkNotClosed();
59554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    validateKey(key);
59654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    Entry entry = lruEntries.get(key);
597e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    if (entry == null) return false;
598e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return removeEntry(entry);
599e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
600e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
601e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private boolean removeEntry(Entry entry) throws IOException {
602e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    if (entry.currentEditor != null) {
603757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer      entry.currentEditor.detach(); // Prevent the edit from completing normally.
604c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
605c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
60654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    for (int i = 0; i < valueCount; i++) {
607a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      fileSystem.delete(entry.cleanFiles[i]);
60854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      size -= entry.lengths[i];
60954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      entry.lengths[i] = 0;
610c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
611c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
61254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    redundantOpCount++;
613e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    journalWriter.writeUtf8(REMOVE).writeByte(' ').writeUtf8(entry.key).writeByte('\n');
614e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    lruEntries.remove(entry.key);
615c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
61654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    if (journalRebuildRequired()) {
617e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      executor.execute(cleanupRunnable);
61854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
619c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
62054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    return true;
62154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
622c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
62354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /** Returns true if this cache has been closed. */
624e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  public synchronized boolean isClosed() {
625e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return closed;
62654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
627c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
628e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private synchronized void checkNotClosed() {
629e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    if (isClosed()) {
63054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      throw new IllegalStateException("cache is closed");
63154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
63254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
63354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
63454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /** Force buffered operations to the filesystem. */
63554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public synchronized void flush() throws IOException {
636e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    if (!initialized) return;
637e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
63854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    checkNotClosed();
63954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    trimToSize();
64054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    journalWriter.flush();
64154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
64254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
64354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /** Closes this cache. Stored values will remain on the filesystem. */
64454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public synchronized void close() throws IOException {
645e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    if (!initialized || closed) {
646e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      closed = true;
647e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      return;
64854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
6493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller    // Copying for safe iteration.
650e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    for (Entry entry : lruEntries.values().toArray(new Entry[lruEntries.size()])) {
65154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      if (entry.currentEditor != null) {
65254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        entry.currentEditor.abort();
65354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
65454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
65554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    trimToSize();
65654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    journalWriter.close();
65754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    journalWriter = null;
658e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    closed = true;
65954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
66054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
66154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void trimToSize() throws IOException {
66254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    while (size > maxSize) {
663e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      Entry toEvict = lruEntries.values().iterator().next();
664e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      removeEntry(toEvict);
66554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
66654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
66754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
66854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /**
66954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * Closes the cache and deletes all of its stored values. This will delete
67054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * all files in the cache directory including files that weren't created by
67154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   * the cache.
67254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson   */
67354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public void delete() throws IOException {
67454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    close();
675a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    fileSystem.deleteContents(directory);
67654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
67754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
678e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /**
679e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * Deletes all stored values from the cache. In-flight edits will complete
680e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * normally but their values will not be stored.
681e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   */
682e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  public synchronized void evictAll() throws IOException {
683e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    initialize();
684e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Copying for safe iteration.
685e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    for (Entry entry : lruEntries.values().toArray(new Entry[lruEntries.size()])) {
686e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      removeEntry(entry);
687e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
688e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
689e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
69054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private void validateKey(String key) {
691faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    Matcher matcher = LEGAL_KEY_PATTERN.matcher(key);
692faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    if (!matcher.matches()) {
693e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      throw new IllegalArgumentException(
694e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          "keys must match regex [a-z0-9_-]{1,120}: \"" + key + "\"");
69554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
69654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
69754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
698e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /**
699e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * Returns an iterator over the cache's current entries. This iterator doesn't throw {@code
700e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * ConcurrentModificationException}, but if new entries are added while iterating, those new
701e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * entries will not be returned by the iterator. If existing entries are removed during iteration,
702e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * they will be absent (unless they were already returned).
703e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   *
704e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * <p>If there are I/O problems during iteration, this iterator fails silently. For example, if
705e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * the hosting filesystem becomes unreachable, the iterator will omit elements rather than
706e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * throwing exceptions.
707e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   *
708e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * <p><strong>The caller must {@link Snapshot#close close}</strong> each snapshot returned by
709e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * {@link Iterator#next}. Failing to do so leaks open files!
710e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   *
711e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * <p>The returned iterator supports {@link Iterator#remove}.
712e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   */
713e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  public synchronized Iterator<Snapshot> snapshots() throws IOException {
714e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    initialize();
715e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return new Iterator<Snapshot>() {
716e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      /** Iterate a copy of the entries to defend against concurrent modification errors. */
717e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      final Iterator<Entry> delegate = new ArrayList<>(lruEntries.values()).iterator();
718e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
719e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      /** The snapshot to return from {@link #next}. Null if we haven't computed that yet. */
720e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      Snapshot nextSnapshot;
721e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
722e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      /** The snapshot to remove with {@link #remove}. Null if removal is illegal. */
723e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      Snapshot removeSnapshot;
724e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
725e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override public boolean hasNext() {
726e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        if (nextSnapshot != null) return true;
727e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
728e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        synchronized (DiskLruCache.this) {
729e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          // If the cache is closed, truncate the iterator.
730e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          if (closed) return false;
731e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
732e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          while (delegate.hasNext()) {
733e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller            Entry entry = delegate.next();
734e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller            Snapshot snapshot = entry.snapshot();
735e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller            if (snapshot == null) continue; // Evicted since we copied the entries.
736e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller            nextSnapshot = snapshot;
737e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller            return true;
738e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          }
739e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        }
740e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
741e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        return false;
742e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
743e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
744e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override public Snapshot next() {
745e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        if (!hasNext()) throw new NoSuchElementException();
746e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        removeSnapshot = nextSnapshot;
747e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        nextSnapshot = null;
748e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        return removeSnapshot;
749e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
750e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
751e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      @Override public void remove() {
752e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        if (removeSnapshot == null) throw new IllegalStateException("remove() before next()");
753e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        try {
754e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          DiskLruCache.this.remove(removeSnapshot.key);
755e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        } catch (IOException ignored) {
756e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          // Nothing useful to do here. We failed to remove from the cache. Most likely that's
757e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          // because we couldn't update the journal, but the cached entry will still be gone.
758e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        } finally {
759e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          removeSnapshot = null;
760e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        }
761e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
762e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    };
76354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
76454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
76554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /** A snapshot of the values for an entry. */
76654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public final class Snapshot implements Closeable {
76754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private final String key;
76854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private final long sequenceNumber;
769e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    private final Source[] sources;
770faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    private final long[] lengths;
77154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
772e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    private Snapshot(String key, long sequenceNumber, Source[] sources, long[] lengths) {
77354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      this.key = key;
77454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      this.sequenceNumber = sequenceNumber;
775e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      this.sources = sources;
776faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      this.lengths = lengths;
777c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
778c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
779e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    public String key() {
780e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      return key;
781e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
782e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
783c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    /**
78454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     * Returns an editor for this snapshot's entry, or null if either the
78554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     * entry has changed since this snapshot was created or if another edit
78654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     * is in progress.
787c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     */
78854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    public Editor edit() throws IOException {
78954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      return DiskLruCache.this.edit(key, sequenceNumber);
790c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
791c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
79254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    /** Returns the unbuffered stream with the value for {@code index}. */
793e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    public Source getSource(int index) {
794e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      return sources[index];
79554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
796c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
797faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    /** Returns the byte length of the value for {@code index}. */
798faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    public long getLength(int index) {
799faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      return lengths[index];
800faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    }
801faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
802faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    public void close() {
803e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      for (Source in : sources) {
80454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        Util.closeQuietly(in);
80554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
80654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
80754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
808c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
809e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private static final Sink NULL_SINK = new Sink() {
810e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    @Override public void write(Buffer source, long byteCount) throws IOException {
811e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      source.skip(byteCount);
812e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
813e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
814e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    @Override public void flush() throws IOException {
815e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
816e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
817e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    @Override public Timeout timeout() {
818e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      return Timeout.NONE;
819e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
820e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
821e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    @Override public void close() throws IOException {
822faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    }
823faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath  };
824faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath
82554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  /** Edits the values for an entry. */
82654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  public final class Editor {
82754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private final Entry entry;
82854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private final boolean[] written;
829757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    private boolean done;
830c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
83154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private Editor(Entry entry) {
83254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      this.entry = entry;
83354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      this.written = (entry.readable) ? null : new boolean[valueCount];
834c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
835c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
836c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    /**
837757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer     * Prevents this editor from completing normally. This is necessary either when the edit causes
838757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer     * an I/O error, or if the target entry is evicted while this editor is active. In either case
839757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer     * we delete the editor's created files and prevent new files from being created. Note that once
840757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer     * an editor has been detached it is possible for another editor to edit the entry.
841757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer     */
842757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    void detach() {
843757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer      if (entry.currentEditor == this) {
844757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer        for (int i = 0; i < valueCount; i++) {
845757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer          try {
846757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer            fileSystem.delete(entry.dirtyFiles[i]);
847757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer          } catch (IOException e) {
848757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer            // This file is potentially leaked. Not much we can do about that.
849757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer          }
850757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer        }
851757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer        entry.currentEditor = null;
852757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer      }
853757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    }
854757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer
855757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    /**
85654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     * Returns an unbuffered input stream to read the last committed value,
85754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     * or null if no value has been committed.
858c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     */
859e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    public Source newSource(int index) throws IOException {
86054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      synchronized (DiskLruCache.this) {
861757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer        if (done) {
86254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          throw new IllegalStateException();
863c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
864757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer        if (!entry.readable || entry.currentEditor != this) {
86554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          return null;
86654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        }
867faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        try {
868a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller          return fileSystem.source(entry.cleanFiles[index]);
869faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        } catch (FileNotFoundException e) {
870faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath          return null;
871faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        }
87254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
873c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
874c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
875c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    /**
87654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     * Returns a new unbuffered output stream to write the value at
87754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     * {@code index}. If the underlying output stream encounters errors
87854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     * when writing to the filesystem, this edit will be aborted when
87954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     * {@link #commit} is called. The returned output stream does not throw
88054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     * IOExceptions.
881c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     */
882e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    public Sink newSink(int index) throws IOException {
88354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      synchronized (DiskLruCache.this) {
884757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer        if (done) {
88554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          throw new IllegalStateException();
886c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
887757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer        if (entry.currentEditor != this) {
888757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer          return NULL_SINK;
889757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer        }
89054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        if (!entry.readable) {
89154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          written[index] = true;
892c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
893e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        File dirtyFile = entry.dirtyFiles[index];
894e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        Sink sink;
895faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        try {
896a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller          sink = fileSystem.sink(dirtyFile);
897faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        } catch (FileNotFoundException e) {
898a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller          return NULL_SINK;
899faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        }
900a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        return new FaultHidingSink(sink) {
901a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller          @Override protected void onException(IOException e) {
902a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller            synchronized (DiskLruCache.this) {
903757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer              detach();
904a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller            }
905a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller          }
906a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        };
90754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
908c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
909c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
910c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    /**
91154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     * Commits this edit so it is visible to readers.  This releases the
91254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     * edit lock so another edit may be started on the same key.
913c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     */
91454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    public void commit() throws IOException {
915e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      synchronized (DiskLruCache.this) {
916757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer        if (done) {
917757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer          throw new IllegalStateException();
918757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer        }
919757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer        if (entry.currentEditor == this) {
920e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          completeEdit(this, true);
921e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        }
922757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer        done = true;
92354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
924c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
925c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
926c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    /**
92754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     * Aborts this edit. This releases the edit lock so another edit may be
92854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson     * started on the same key.
929c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath     */
93054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    public void abort() throws IOException {
931e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      synchronized (DiskLruCache.this) {
932757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer        if (done) {
933757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer          throw new IllegalStateException();
934757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer        }
935757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer        if (entry.currentEditor == this) {
936757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer          completeEdit(this, false);
937757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer        }
938757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer        done = true;
939e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
940c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
941c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
942faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    public void abortUnlessCommitted() {
943e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      synchronized (DiskLruCache.this) {
944757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer        if (!done && entry.currentEditor == this) {
945e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          try {
946e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller            completeEdit(this, false);
947e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          } catch (IOException ignored) {
948e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          }
949faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath        }
950faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath      }
951faf49723fb689c626f69876e718c58018eff8ee7Narayan Kamath    }
95254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
953c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
95454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  private final class Entry {
95554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private final String key;
956c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
95754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    /** Lengths of this entry's files. */
95854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private final long[] lengths;
959e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    private final File[] cleanFiles;
960e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    private final File[] dirtyFiles;
961c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
96254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    /** True if this entry has ever been published. */
96354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private boolean readable;
964c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
96554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    /** The ongoing edit or null if this entry is not being edited. */
96654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private Editor currentEditor;
967c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
96854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    /** The sequence number of the most recently committed edit to this entry. */
96954cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private long sequenceNumber;
970c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
97154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private Entry(String key) {
97254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      this.key = key;
973c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
974e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      lengths = new long[valueCount];
975e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      cleanFiles = new File[valueCount];
976e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      dirtyFiles = new File[valueCount];
977e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
978e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      // The names are repetitive so re-use the same builder to avoid allocations.
979e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      StringBuilder fileBuilder = new StringBuilder(key).append('.');
980e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      int truncateTo = fileBuilder.length();
981e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      for (int i = 0; i < valueCount; i++) {
982e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        fileBuilder.append(i);
983e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        cleanFiles[i] = new File(directory, fileBuilder.toString());
984e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        fileBuilder.append(".tmp");
985e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        dirtyFiles[i] = new File(directory, fileBuilder.toString());
986e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        fileBuilder.setLength(truncateTo);
98754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
98854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
989c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
99054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    /** Set lengths using decimal numbers like "10123". */
99154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    private void setLengths(String[] strings) throws IOException {
99254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      if (strings.length != valueCount) {
99354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        throw invalidLengths(strings);
99454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
995c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
99654cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      try {
99754cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        for (int i = 0; i < strings.length; i++) {
99854cf3446000fdcf88a9e62724f7deb0282e98da1jwilson          lengths[i] = Long.parseLong(strings[i]);
999c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath        }
100054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      } catch (NumberFormatException e) {
100154cf3446000fdcf88a9e62724f7deb0282e98da1jwilson        throw invalidLengths(strings);
100254cf3446000fdcf88a9e62724f7deb0282e98da1jwilson      }
100354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
1004c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
1005e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    /** Append space-prefixed lengths to {@code writer}. */
1006e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    void writeLengths(BufferedSink writer) throws IOException {
1007e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      for (long length : lengths) {
1008a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller        writer.writeByte(' ').writeDecimalLong(length);
1009e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
101054cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
1011c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath
1012e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    private IOException invalidLengths(String[] strings) throws IOException {
1013e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      throw new IOException("unexpected journal line: " + Arrays.toString(strings));
101454cf3446000fdcf88a9e62724f7deb0282e98da1jwilson    }
101554cf3446000fdcf88a9e62724f7deb0282e98da1jwilson
1016e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    /**
1017e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller     * Returns a snapshot of this entry. This opens all streams eagerly to guarantee that we see a
1018e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller     * single published snapshot. If we opened streams lazily then the streams could come from
1019e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller     * different edits.
1020e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller     */
1021e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Snapshot snapshot() {
1022e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      if (!Thread.holdsLock(DiskLruCache.this)) throw new AssertionError();
1023e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1024e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      Source[] sources = new Source[valueCount];
1025e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      long[] lengths = this.lengths.clone(); // Defensive copy since these can be zeroed out.
1026e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      try {
1027e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        for (int i = 0; i < valueCount; i++) {
1028a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller          sources[i] = fileSystem.source(cleanFiles[i]);
1029e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        }
1030e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        return new Snapshot(key, sequenceNumber, sources, lengths);
1031e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      } catch (FileNotFoundException e) {
1032e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        // A file must have been deleted manually!
1033e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        for (int i = 0; i < valueCount; i++) {
1034e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          if (sources[i] != null) {
1035e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller            Util.closeQuietly(sources[i]);
1036e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          } else {
1037e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller            break;
1038e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          }
1039e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        }
1040e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        return null;
1041e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      }
1042c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath    }
104354cf3446000fdcf88a9e62724f7deb0282e98da1jwilson  }
1044c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath}
1045