Package org.eclipse.jgit.storage.file

Source Code of org.eclipse.jgit.storage.file.UnpackedObject$LargeObject

/*
* Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
* Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org>
* Copyright (C) 2010, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
* under the terms of the Eclipse Distribution License v1.0 which
* accompanies this distribution, is reproduced below, and is
* available at http://www.eclipse.org/org/documents/edl-v10.php
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright
*   notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above
*   copyright notice, this list of conditions and the following
*   disclaimer in the documentation and/or other materials provided
*   with the distribution.
*
* - Neither the name of the Eclipse Foundation, Inc. nor the
*   names of its contributors may be used to endorse or promote
*   products derived from this software without specific prior
*   written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package org.eclipse.jgit.storage.file;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
import java.util.zip.ZipException;

import org.eclipse.jgit.JGitText;
import org.eclipse.jgit.errors.CorruptObjectException;
import org.eclipse.jgit.errors.LargeObjectException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.InflaterCache;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.ObjectStream;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.MutableInteger;
import org.eclipse.jgit.util.RawParseUtils;

/**
* Loose object loader. This class loads an object not stored in a pack.
*/
public class UnpackedObject {
  private static final int BUFFER_SIZE = 8192;

  /**
   * Parse an object from the unpacked object format.
   *
   * @param raw
   *            complete contents of the compressed object.
   * @param id
   *            expected ObjectId of the object, used only for error reporting
   *            in exceptions.
   * @return loader to read the inflated contents.
   * @throws IOException
   *             the object cannot be parsed.
   */
  public static ObjectLoader parse(byte[] raw, AnyObjectId id)
      throws IOException {
    WindowCursor wc = new WindowCursor(null);
    try {
      return open(new ByteArrayInputStream(raw), null, id, wc);
    } finally {
      wc.release();
    }
  }

  static ObjectLoader open(InputStream in, File path, AnyObjectId id,
      WindowCursor wc) throws IOException {
    try {
      in = buffer(in);
      in.mark(20);
      final byte[] hdr = new byte[64];
      IO.readFully(in, hdr, 0, 2);

      if (isStandardFormat(hdr)) {
        in.reset();
        Inflater inf = wc.inflater();
        InputStream zIn = inflate(in, inf);
        int avail = readSome(zIn, hdr, 0, 64);
        if (avail < 5)
          throw new CorruptObjectException(id,
              JGitText.get().corruptObjectNoHeader);

        final MutableInteger p = new MutableInteger();
        int type = Constants.decodeTypeString(id, hdr, (byte) ' ', p);
        long size = RawParseUtils.parseLongBase10(hdr, p.value, p);
        if (size < 0)
          throw new CorruptObjectException(id,
              JGitText.get().corruptObjectNegativeSize);
        if (hdr[p.value++] != 0)
          throw new CorruptObjectException(id,
              JGitText.get().corruptObjectGarbageAfterSize);
        if (path == null && Integer.MAX_VALUE < size) {
          LargeObjectException.ExceedsByteArrayLimit e;
          e = new LargeObjectException.ExceedsByteArrayLimit();
          e.setObjectId(id);
          throw e;
        }
        if (size < wc.getStreamFileThreshold() || path == null) {
          byte[] data = new byte[(int) size];
          int n = avail - p.value;
          if (n > 0)
            System.arraycopy(hdr, p.value, data, 0, n);
          IO.readFully(zIn, data, n, data.length - n);
          checkValidEndOfStream(in, inf, id, hdr);
          return new ObjectLoader.SmallObject(type, data);
        }
        return new LargeObject(type, size, path, id, wc.db);

      } else {
        readSome(in, hdr, 2, 18);
        int c = hdr[0] & 0xff;
        int type = (c >> 4) & 7;
        long size = c & 15;
        int shift = 4;
        int p = 1;
        while ((c & 0x80) != 0) {
          c = hdr[p++] & 0xff;
          size += (c & 0x7f) << shift;
          shift += 7;
        }

        switch (type) {
        case Constants.OBJ_COMMIT:
        case Constants.OBJ_TREE:
        case Constants.OBJ_BLOB:
        case Constants.OBJ_TAG:
          // Acceptable types for a loose object.
          break;
        default:
          throw new CorruptObjectException(id,
              JGitText.get().corruptObjectInvalidType);
        }

        if (path == null && Integer.MAX_VALUE < size) {
          LargeObjectException.ExceedsByteArrayLimit e;
          e = new LargeObjectException.ExceedsByteArrayLimit();
          e.setObjectId(id);
          throw e;
        }
        if (size < wc.getStreamFileThreshold() || path == null) {
          in.reset();
          IO.skipFully(in, p);
          Inflater inf = wc.inflater();
          InputStream zIn = inflate(in, inf);
          byte[] data = new byte[(int) size];
          IO.readFully(zIn, data, 0, data.length);
          checkValidEndOfStream(in, inf, id, hdr);
          return new ObjectLoader.SmallObject(type, data);
        }
        return new LargeObject(type, size, path, id, wc.db);
      }
    } catch (ZipException badStream) {
      throw new CorruptObjectException(id,
          JGitText.get().corruptObjectBadStream);
    }
  }

  static long getSize(InputStream in, AnyObjectId id, WindowCursor wc)
      throws IOException {
    try {
      in = buffer(in);
      in.mark(20);
      final byte[] hdr = new byte[64];
      IO.readFully(in, hdr, 0, 2);

      if (isStandardFormat(hdr)) {
        in.reset();
        Inflater inf = wc.inflater();
        InputStream zIn = inflate(in, inf);
        int avail = readSome(zIn, hdr, 0, 64);
        if (avail < 5)
          throw new CorruptObjectException(id,
              JGitText.get().corruptObjectNoHeader);

        final MutableInteger p = new MutableInteger();
        Constants.decodeTypeString(id, hdr, (byte) ' ', p);
        long size = RawParseUtils.parseLongBase10(hdr, p.value, p);
        if (size < 0)
          throw new CorruptObjectException(id,
              JGitText.get().corruptObjectNegativeSize);
        return size;

      } else {
        readSome(in, hdr, 2, 18);
        int c = hdr[0] & 0xff;
        long size = c & 15;
        int shift = 4;
        int p = 1;
        while ((c & 0x80) != 0) {
          c = hdr[p++] & 0xff;
          size += (c & 0x7f) << shift;
          shift += 7;
        }
        return size;
      }
    } catch (ZipException badStream) {
      throw new CorruptObjectException(id,
          JGitText.get().corruptObjectBadStream);
    }
  }

  private static void checkValidEndOfStream(InputStream in, Inflater inf,
      AnyObjectId id, final byte[] buf) throws IOException,
      CorruptObjectException {
    for (;;) {
      int r;
      try {
        r = inf.inflate(buf);
      } catch (DataFormatException e) {
        throw new CorruptObjectException(id,
            JGitText.get().corruptObjectBadStream);
      }
      if (r != 0)
        throw new CorruptObjectException(id,
            JGitText.get().corruptObjectIncorrectLength);

      if (inf.finished()) {
        if (inf.getRemaining() != 0 || in.read() != -1)
          throw new CorruptObjectException(id,
              JGitText.get().corruptObjectBadStream);
        break;
      }

      if (!inf.needsInput())
        throw new CorruptObjectException(id,
            JGitText.get().corruptObjectBadStream);

      r = in.read(buf);
      if (r <= 0)
        throw new CorruptObjectException(id,
            JGitText.get().corruptObjectBadStream);
      inf.setInput(buf, 0, r);
    }
  }

  private static boolean isStandardFormat(final byte[] hdr) {
    // Try to determine if this is a standard format loose object or
    // a pack style loose object. The standard format is completely
    // compressed with zlib so the first byte must be 0x78 (15-bit
    // window size, deflated) and the first 16 bit word must be
    // evenly divisible by 31. Otherwise its a pack style object.
    //
    final int fb = hdr[0] & 0xff;
    return fb == 0x78 && (((fb << 8) | hdr[1] & 0xff) % 31) == 0;
  }

  private static InputStream inflate(final InputStream in, final long size,
      final ObjectId id) {
    final Inflater inf = InflaterCache.get();
    return new InflaterInputStream(in, inf) {
      private long remaining = size;

      @Override
      public int read(byte[] b, int off, int cnt) throws IOException {
        try {
          int r = super.read(b, off, cnt);
          if (r > 0)
            remaining -= r;
          return r;
        } catch (ZipException badStream) {
          throw new CorruptObjectException(id,
              JGitText.get().corruptObjectBadStream);
        }
      }

      @Override
      public void close() throws IOException {
        try {
          if (remaining <= 0)
            checkValidEndOfStream(in, inf, id, new byte[64]);
        } finally {
          InflaterCache.release(inf);
          super.close();
        }
      }
    };
  }

  private static InflaterInputStream inflate(InputStream in, Inflater inf) {
    return new InflaterInputStream(in, inf, BUFFER_SIZE);
  }

  private static BufferedInputStream buffer(InputStream in) {
    return new BufferedInputStream(in, BUFFER_SIZE);
  }

  private static int readSome(InputStream in, final byte[] hdr, int off,
      int cnt) throws IOException {
    int avail = 0;
    while (0 < cnt) {
      int n = in.read(hdr, off, cnt);
      if (n < 0)
        break;
      avail += n;
      off += n;
      cnt -= n;
    }
    return avail;
  }

  private static final class LargeObject extends ObjectLoader {
    private final int type;

    private final long size;

    private final File path;

    private final ObjectId id;

    private final FileObjectDatabase source;

    private LargeObject(int type, long size, File path, AnyObjectId id,
        FileObjectDatabase db) {
      this.type = type;
      this.size = size;
      this.path = path;
      this.id = id.copy();
      this.source = db;
    }

    @Override
    public int getType() {
      return type;
    }

    @Override
    public long getSize() {
      return size;
    }

    @Override
    public boolean isLarge() {
      return true;
    }

    @Override
    public byte[] getCachedBytes() throws LargeObjectException {
      throw new LargeObjectException(id);
    }

    @Override
    public ObjectStream openStream() throws MissingObjectException,
        IOException {
      InputStream in;
      try {
        in = buffer(new FileInputStream(path));
      } catch (FileNotFoundException gone) {
        // If the loose file no longer exists, it may have been
        // moved into a pack file in the mean time. Try again
        // to locate the object.
        //
        return source.open(id, type).openStream();
      }

      boolean ok = false;
      try {
        final byte[] hdr = new byte[64];
        in.mark(20);
        IO.readFully(in, hdr, 0, 2);

        if (isStandardFormat(hdr)) {
          in.reset();
          in = buffer(inflate(in, size, id));
          while (0 < in.read())
            continue;
        } else {
          readSome(in, hdr, 2, 18);
          int c = hdr[0] & 0xff;
          int p = 1;
          while ((c & 0x80) != 0)
            c = hdr[p++] & 0xff;

          in.reset();
          IO.skipFully(in, p);
          in = buffer(inflate(in, size, id));
        }

        ok = true;
        return new ObjectStream.Filter(type, size, in);
      } finally {
        if (!ok)
          in.close();
      }
    }
  }
}
TOP

Related Classes of org.eclipse.jgit.storage.file.UnpackedObject$LargeObject

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.