DeltaBaseCache.java

  1. /*
  2.  * Copyright (C) 2011, Google Inc. and others
  3.  *
  4.  * This program and the accompanying materials are made available under the
  5.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  6.  * https://www.eclipse.org/org/documents/edl-v10.php.
  7.  *
  8.  * SPDX-License-Identifier: BSD-3-Clause
  9.  */

  10. package org.eclipse.jgit.internal.storage.dfs;


  11. /**
  12.  * Caches recently used objects for {@link DfsReader}.
  13.  * <p>
  14.  * This cache is not thread-safe. Each reader should have its own cache.
  15.  */
  16. final class DeltaBaseCache {
  17.     private static final int TABLE_BITS = 10;

  18.     private static final int MASK_BITS = 32 - TABLE_BITS;

  19.     private static int hash(long position) {
  20.         return (((int) position) << MASK_BITS) >>> MASK_BITS;
  21.     }

  22.     private int maxByteCount;
  23.     private int curByteCount;

  24.     private final Entry[] table;

  25.     private Entry lruHead;
  26.     private Entry lruTail;

  27.     DeltaBaseCache(DfsReader reader) {
  28.         this(reader.getOptions().getDeltaBaseCacheLimit());
  29.     }

  30.     DeltaBaseCache(int maxBytes) {
  31.         maxByteCount = maxBytes;
  32.         table = new Entry[1 << TABLE_BITS];
  33.     }

  34.     Entry get(DfsStreamKey key, long position) {
  35.         Entry e = table[hash(position)];
  36.         for (; e != null; e = e.tableNext) {
  37.             if (e.offset == position && key.equals(e.pack)) {
  38.                 moveToHead(e);
  39.                 return e;
  40.             }
  41.         }
  42.         return null;
  43.     }

  44.     void put(DfsStreamKey key, long offset, int objectType, byte[] data) {
  45.         if (data.length > maxByteCount)
  46.             return; // Too large to cache.

  47.         curByteCount += data.length;
  48.         releaseMemory();

  49.         int tableIdx = hash(offset);
  50.         Entry e = new Entry(key, offset, objectType, data);
  51.         e.tableNext = table[tableIdx];
  52.         table[tableIdx] = e;
  53.         lruPushHead(e);
  54.     }

  55.     private void releaseMemory() {
  56.         while (curByteCount > maxByteCount && lruTail != null) {
  57.             Entry e = lruTail;
  58.             curByteCount -= e.data.length;
  59.             lruRemove(e);
  60.             removeFromTable(e);
  61.         }
  62.     }

  63.     private void removeFromTable(Entry e) {
  64.         int tableIdx = hash(e.offset);
  65.         Entry p = table[tableIdx];

  66.         if (p == e) {
  67.             table[tableIdx] = e.tableNext;
  68.             return;
  69.         }

  70.         for (; p != null; p = p.tableNext) {
  71.             if (p.tableNext == e) {
  72.                 p.tableNext = e.tableNext;
  73.                 return;
  74.             }
  75.         }

  76.         throw new IllegalStateException(String.format(
  77.                 "entry for %s:%d not in table", //$NON-NLS-1$
  78.                 e.pack, Long.valueOf(e.offset)));
  79.     }

  80.     private void moveToHead(Entry e) {
  81.         if (e != lruHead) {
  82.             lruRemove(e);
  83.             lruPushHead(e);
  84.         }
  85.     }

  86.     private void lruRemove(Entry e) {
  87.         Entry p = e.lruPrev;
  88.         Entry n = e.lruNext;

  89.         if (p != null) {
  90.             p.lruNext = n;
  91.         } else {
  92.             lruHead = n;
  93.         }

  94.         if (n != null) {
  95.             n.lruPrev = p;
  96.         } else {
  97.             lruTail = p;
  98.         }
  99.     }

  100.     private void lruPushHead(Entry e) {
  101.         Entry n = lruHead;
  102.         e.lruNext = n;
  103.         if (n != null)
  104.             n.lruPrev = e;
  105.         else
  106.             lruTail = e;

  107.         e.lruPrev = null;
  108.         lruHead = e;
  109.     }

  110.     int getMemoryUsed() {
  111.         return curByteCount;
  112.     }

  113.     int getMemoryUsedByLruChainForTest() {
  114.         int r = 0;
  115.         for (Entry e = lruHead; e != null; e = e.lruNext) {
  116.             r += e.data.length;
  117.         }
  118.         return r;
  119.     }

  120.     int getMemoryUsedByTableForTest() {
  121.         int r = 0;
  122.         for (Entry t : table) {
  123.             for (Entry e = t; e != null; e = e.tableNext) {
  124.                 r += e.data.length;
  125.             }
  126.         }
  127.         return r;
  128.     }

  129.     static class Entry {
  130.         final DfsStreamKey pack;
  131.         final long offset;
  132.         final int type;
  133.         final byte[] data;

  134.         Entry tableNext;
  135.         Entry lruPrev;
  136.         Entry lruNext;

  137.         Entry(DfsStreamKey key, long offset, int type, byte[] data) {
  138.             this.pack = key;
  139.             this.offset = offset;
  140.             this.type = type;
  141.             this.data = data;
  142.         }
  143.     }
  144. }