CachedObjectDirectory.java

  1. /*
  2.  * Copyright (C) 2010, Constantine Plotnikov <constantine.plotnikov@gmail.com>
  3.  * Copyright (C) 2010, JetBrains s.r.o. and others
  4.  *
  5.  * This program and the accompanying materials are made available under the
  6.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  7.  * https://www.eclipse.org/org/documents/edl-v10.php.
  8.  *
  9.  * SPDX-License-Identifier: BSD-3-Clause
  10.  */

  11. package org.eclipse.jgit.internal.storage.file;

  12. import java.io.File;
  13. import java.io.IOException;
  14. import java.util.Collection;
  15. import java.util.HashSet;
  16. import java.util.Set;

  17. import org.eclipse.jgit.internal.storage.file.ObjectDirectory.AlternateHandle;
  18. import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
  19. import org.eclipse.jgit.internal.storage.pack.PackWriter;
  20. import org.eclipse.jgit.lib.AbbreviatedObjectId;
  21. import org.eclipse.jgit.lib.AnyObjectId;
  22. import org.eclipse.jgit.lib.Config;
  23. import org.eclipse.jgit.lib.Constants;
  24. import org.eclipse.jgit.lib.ObjectDatabase;
  25. import org.eclipse.jgit.lib.ObjectId;
  26. import org.eclipse.jgit.lib.ObjectIdOwnerMap;
  27. import org.eclipse.jgit.lib.ObjectLoader;
  28. import org.eclipse.jgit.util.FS;

  29. /**
  30.  * The cached instance of an {@link ObjectDirectory}.
  31.  * <p>
  32.  * This class caches the list of loose objects in memory, so the file system is
  33.  * not queried with stat calls.
  34.  */
  35. class CachedObjectDirectory extends FileObjectDatabase {
  36.     /**
  37.      * The set that contains unpacked objects identifiers, it is created when
  38.      * the cached instance is created.
  39.      */
  40.     private ObjectIdOwnerMap<UnpackedObjectId> unpackedObjects;

  41.     private final ObjectDirectory wrapped;

  42.     private CachedObjectDirectory[] alts;

  43.     /**
  44.      * The constructor
  45.      *
  46.      * @param wrapped
  47.      *            the wrapped database
  48.      */
  49.     CachedObjectDirectory(ObjectDirectory wrapped) {
  50.         this.wrapped = wrapped;
  51.         this.unpackedObjects = scanLoose();
  52.     }

  53.     private ObjectIdOwnerMap<UnpackedObjectId> scanLoose() {
  54.         ObjectIdOwnerMap<UnpackedObjectId> m = new ObjectIdOwnerMap<>();
  55.         File objects = wrapped.getDirectory();
  56.         String[] fanout = objects.list();
  57.         if (fanout == null)
  58.             return m;
  59.         for (String d : fanout) {
  60.             if (d.length() != 2)
  61.                 continue;
  62.             String[] entries = new File(objects, d).list();
  63.             if (entries == null)
  64.                 continue;
  65.             for (String e : entries) {
  66.                 if (e.length() != Constants.OBJECT_ID_STRING_LENGTH - 2)
  67.                     continue;
  68.                 try {
  69.                     ObjectId id = ObjectId.fromString(d + e);
  70.                     m.add(new UnpackedObjectId(id));
  71.                 } catch (IllegalArgumentException notAnObject) {
  72.                     // ignoring the file that does not represent loose object
  73.                 }
  74.             }
  75.         }
  76.         return m;
  77.     }

  78.     /** {@inheritDoc} */
  79.     @Override
  80.     public void close() {
  81.         // Don't close anything.
  82.     }

  83.     /** {@inheritDoc} */
  84.     @Override
  85.     public ObjectDatabase newCachedDatabase() {
  86.         return this;
  87.     }

  88.     @Override
  89.     File getDirectory() {
  90.         return wrapped.getDirectory();
  91.     }

  92.     @Override
  93.     File fileFor(AnyObjectId id) {
  94.         return wrapped.fileFor(id);
  95.     }

  96.     @Override
  97.     Config getConfig() {
  98.         return wrapped.getConfig();
  99.     }

  100.     @Override
  101.     FS getFS() {
  102.         return wrapped.getFS();
  103.     }

  104.     @Override
  105.     Set<ObjectId> getShallowCommits() throws IOException {
  106.         return wrapped.getShallowCommits();
  107.     }

  108.     private CachedObjectDirectory[] myAlternates() {
  109.         if (alts == null) {
  110.             ObjectDirectory.AlternateHandle[] src = wrapped.myAlternates();
  111.             alts = new CachedObjectDirectory[src.length];
  112.             for (int i = 0; i < alts.length; i++)
  113.                 alts[i] = src[i].db.newCachedFileObjectDatabase();
  114.         }
  115.         return alts;
  116.     }

  117.     private Set<AlternateHandle.Id> skipMe(Set<AlternateHandle.Id> skips) {
  118.         Set<AlternateHandle.Id> withMe = new HashSet<>();
  119.         if (skips != null) {
  120.             withMe.addAll(skips);
  121.         }
  122.         withMe.add(getAlternateId());
  123.         return withMe;
  124.     }

  125.     @Override
  126.     void resolve(Set<ObjectId> matches, AbbreviatedObjectId id)
  127.             throws IOException {
  128.         wrapped.resolve(matches, id);
  129.     }

  130.     /** {@inheritDoc} */
  131.     @Override
  132.     public boolean has(AnyObjectId objectId) throws IOException {
  133.         return has(objectId, null);
  134.     }

  135.     private boolean has(AnyObjectId objectId, Set<AlternateHandle.Id> skips)
  136.             throws IOException {
  137.         if (unpackedObjects.contains(objectId)) {
  138.             return true;
  139.         }
  140.         if (wrapped.hasPackedObject(objectId)) {
  141.             return true;
  142.         }
  143.         skips = skipMe(skips);
  144.         for (CachedObjectDirectory alt : myAlternates()) {
  145.             if (!skips.contains(alt.getAlternateId())) {
  146.                 if (alt.has(objectId, skips)) {
  147.                     return true;
  148.                 }
  149.             }
  150.         }
  151.         return false;
  152.     }

  153.     @Override
  154.     ObjectLoader openObject(WindowCursor curs, AnyObjectId objectId)
  155.             throws IOException {
  156.         return openObject(curs, objectId, null);
  157.     }

  158.     private ObjectLoader openObject(final WindowCursor curs,
  159.             final AnyObjectId objectId, Set<AlternateHandle.Id> skips)
  160.             throws IOException {
  161.         ObjectLoader ldr = openLooseObject(curs, objectId);
  162.         if (ldr != null) {
  163.             return ldr;
  164.         }
  165.         ldr = wrapped.openPackedObject(curs, objectId);
  166.         if (ldr != null) {
  167.             return ldr;
  168.         }
  169.         skips = skipMe(skips);
  170.         for (CachedObjectDirectory alt : myAlternates()) {
  171.             if (!skips.contains(alt.getAlternateId())) {
  172.                 ldr = alt.openObject(curs, objectId, skips);
  173.                 if (ldr != null) {
  174.                     return ldr;
  175.                 }
  176.             }
  177.         }
  178.         return null;
  179.     }

  180.     @Override
  181.     long getObjectSize(WindowCursor curs, AnyObjectId objectId)
  182.             throws IOException {
  183.         // Object size is unlikely to be requested from contexts using
  184.         // this type. Don't bother trying to accelerate the lookup.
  185.         return wrapped.getObjectSize(curs, objectId);
  186.     }

  187.     @Override
  188.     ObjectLoader openLooseObject(WindowCursor curs, AnyObjectId id)
  189.             throws IOException {
  190.         if (unpackedObjects.contains(id)) {
  191.             ObjectLoader ldr = wrapped.openLooseObject(curs, id);
  192.             if (ldr != null)
  193.                 return ldr;
  194.             unpackedObjects = scanLoose();
  195.         }
  196.         return null;
  197.     }

  198.     @Override
  199.     InsertLooseObjectResult insertUnpackedObject(File tmp, ObjectId objectId,
  200.             boolean createDuplicate) throws IOException {
  201.         InsertLooseObjectResult result = wrapped.insertUnpackedObject(tmp,
  202.                 objectId, createDuplicate);
  203.         switch (result) {
  204.         case INSERTED:
  205.         case EXISTS_LOOSE:
  206.             unpackedObjects.addIfAbsent(new UnpackedObjectId(objectId));
  207.             break;

  208.         case EXISTS_PACKED:
  209.         case FAILURE:
  210.             break;
  211.         }
  212.         return result;
  213.     }

  214.     @Override
  215.     Pack openPack(File pack) throws IOException {
  216.         return wrapped.openPack(pack);
  217.     }

  218.     @Override
  219.     void selectObjectRepresentation(PackWriter packer, ObjectToPack otp,
  220.             WindowCursor curs) throws IOException {
  221.         wrapped.selectObjectRepresentation(packer, otp, curs);
  222.     }

  223.     @Override
  224.     Collection<Pack> getPacks() {
  225.         return wrapped.getPacks();
  226.     }

  227.     private static class UnpackedObjectId extends ObjectIdOwnerMap.Entry {
  228.         UnpackedObjectId(AnyObjectId id) {
  229.             super(id);
  230.         }
  231.     }

  232.     private AlternateHandle.Id getAlternateId() {
  233.         return wrapped.getAlternateId();
  234.     }
  235. }