예제 #1
0
        public PackFile(FileInfo packFi)
        {
            mPackFile = packFi;

            string idxPath = packFi.FullName.Substring(0, packFi.FullName.Length - packFi.Extension.Length);
            idxPath += ".idx";
            var idxFi = new FileInfo(idxPath);
            if (!idxFi.Exists)
                throw new FileNotFoundException("Could not find index.");

            ObjectId packfileHash = ObjectId.Parse(packFi.Name.Substring(5, 40)); //pack-{hash}
            int numberOfObjects;

            using (var fs = packFi.OpenRead())
            {
                if (fs.ReadByte() != 'P')
                    throw new Exception("Bad header");
                if (fs.ReadByte() != 'A')
                    throw new Exception("Bad header");
                if (fs.ReadByte() != 'C')
                    throw new Exception("Bad header");
                if (fs.ReadByte() != 'K')
                    throw new Exception("Bad header");

                var br = new NetworkByteOrderBinaryReader(fs);

                if (br.ReadInt32() != 2)
                    throw new Exception("Bad pack version.");

                numberOfObjects = br.ReadInt32();

                if (numberOfObjects < 0)
                {
                    //this assumption makes things a lot easier, like we can use the built-in binary search function.
                    throw new NotImplementedException("Support for more then 2^31 obects in a pack file is not implemented.");
                }

                //read the hash from the end
                fs.Seek(-20, SeekOrigin.End);
                if (!packfileHash.Equals(ObjectId.ReadFromStream(fs)))
                    throw new Exception("Packfile stored hash does not match file name.");

                //check the hash
                fs.Seek(0, SeekOrigin.Begin);
                var computedHash = Sha1.ComputeHash(new SubsetStream(fs, fs.Length - 20));
                if (!computedHash.Equals(packfileHash))
                    throw new Exception("Hash of pack contents does match stored contents or filename.");
            }

            mFanOut = new int[256];
            mObjectIds = new ObjectId[numberOfObjects];
            mOffsets = new uint[numberOfObjects];

            using (var fs = idxFi.OpenRead())
            {
                //check the pack checksum
                fs.Seek(-40, SeekOrigin.End);
                if (ObjectId.ReadFromStream(fs) != packfileHash)
                    throw new Exception("Index's copy of the pack file hash does not match.");
                ObjectId indexHash = ObjectId.ReadFromStream(fs);

                //check the index file checksum
                fs.Seek(0, SeekOrigin.Begin);
                if (indexHash != Sha1.ComputeHash(new SubsetStream(fs, fs.Length - 20)))
                    throw new Exception("Checksum of index file is not correct.");

                //load all the data now
                fs.Seek(0, SeekOrigin.Begin);

                //check header \377tOc
                if (fs.ReadByte() != 0xff)
                    throw new Exception("Bad header");
                if (fs.ReadByte() != 't')
                    throw new Exception("Bad header");
                if (fs.ReadByte() != 'O')
                    throw new Exception("Bad header");
                if (fs.ReadByte() != 'c')
                    throw new Exception("Bad header");

                var br = new NetworkByteOrderBinaryReader(fs);
                int version = br.ReadInt32();

                if (version != 2)
                    throw new Exception("Unexpected pack index version: " + version);

                for (int i = 0; i < mFanOut.Length; i++)
                {
                    mFanOut[i] = br.ReadInt32();
                }

                for (int i = 0; i < numberOfObjects; i++)
                {
                    mObjectIds[i] = ObjectId.ReadFromStream(fs);
                    //if (i != 0 && mObjectIds[i-1].c)
                }

                //skip past
                fs.Seek((long)numberOfObjects * 4, SeekOrigin.Current);

                int bigFileCount = 0;
                for (int i = 0; i < numberOfObjects; i++)
                {
                    var oft = br.ReadUInt32();
                    mOffsets[i] = oft;
                    if ((oft & 0x80000000) != 0)
                        bigFileCount++;
                }

                mBigOffsets = new long[bigFileCount];
                for (int i = 0; i < bigFileCount; i++)
                {
                    Debug.Assert(false, "this is as of yet untested, so test it and remove this assert");
                    mBigOffsets[i] = br.ReadInt64();
                }
            }
        }
예제 #2
0
        public Tuple<PackObjectType, byte[]> ReadObject(long offset)
        {
            using (var fs = mPackFile.OpenRead())
            {
                var br = new NetworkByteOrderBinaryReader(fs);

                fs.Seek(offset, SeekOrigin.Begin);

                byte b = br.ReadByte();
                var type = (PackObjectType)((b >> 4) & 0x7);
                int uncompressedSize = b & 0xf;
                int shift = 4;
                while ((b & 0x80) == 0x80)
                {
                    if (shift >= 25)
                        throw new Exception("Object size does nto fit in a 32-bit integer.");
                    b = br.ReadByte();
                    uncompressedSize |= ((b & 0x7f) << shift);
                    shift += 7;
                }

                Tuple<PackObjectType, byte[]> deltaBasis = null;

                if (type == PackObjectType.OfsDelta)
                {
                    b = br.ReadByte();
                    int basisOffset = b & 0x7f;
                    while ((b & 0x80) == 0x80)
                    {
                        //TODO: check overflow
                        basisOffset += 1;
                        b = br.ReadByte();
                        basisOffset <<= 7;
                        basisOffset |= (b & 0x7f);
                    }
                    deltaBasis = ReadObject(offset - basisOffset);
                }
                else if (type == PackObjectType.RefDelta)
                {
                    var basisId = ObjectId.ReadFromStream(fs);
                    deltaBasis = ReadObject(basisId);
                }
                else if (type != PackObjectType.Blob
                      && type != PackObjectType.Commit
                      && type != PackObjectType.Tree
                      && type != PackObjectType.Tag)
                {
                    throw new Exception("Unexpected object type: " + type);
                }

                byte[] decompressedObject = new byte[uncompressedSize];
                using (var inflator = new InflaterInputStream(fs))
                {
                    var bytesRead = inflator.Read(decompressedObject, 0, uncompressedSize);
                    if (uncompressedSize != bytesRead)
                        throw new Exception("Short read.");
                }

                if (deltaBasis == null)
                    return new Tuple<PackObjectType, byte[]>(type, decompressedObject);
                else
                    return new Tuple<PackObjectType, byte[]>(deltaBasis.Item1, applyDelta(deltaBasis.Item2, decompressedObject));
            }
        }