Example #1
0
        /// <summary>
        /// If available, overwrites the space allocated to the previous file first to save space.
        /// </summary>
        /// <param name="index"></param>
        /// <param name="fileId"></param>
        /// <param name="data"></param>
        public void WriteFileData(Index index, int fileId, byte[] data)
        {
            if (this.ReadOnly)
            {
                throw new InvalidOperationException("Can't write data in readonly mode.");
            }

            lock (this._ioLock)
            {
                // Obtain possibly existing sector positions to overwrite
                int[] existingSectorPositions;
                try
                {
                    existingSectorPositions = this.ReadSectors(index, fileId)
                                              .Select(sector => sector.Position)
                                              .ToArray();
                }
                catch (Exception ex) when(ex is FileNotFoundException)
                {
                    // Assume there are no existing sectors when the method fails
                    existingSectorPositions = new int[0];
                }

                var sectors = Sector.FromData(data, index, fileId);

                var dataWriter = new BinaryWriter(this._dataStream);

                foreach (var sector in sectors)
                {
                    // Overwrite existing sector data if available, otherwise append to file
                    sector.Position = sector.ChunkId < existingSectorPositions.Length
                        ? existingSectorPositions[sector.ChunkId]
                        : (int)(dataWriter.BaseStream.Length / Sector.Length);

                    // Set position of next sector
                    sector.NextSectorPosition = sector.ChunkId + 1 < existingSectorPositions.Length
                        ? existingSectorPositions[sector.ChunkId + 1]
                        : (int)(dataWriter.BaseStream.Length / Sector.Length);

                    // Happens if both positions were based on the stream length
                    if (sector.NextSectorPosition == sector.Position)
                    {
                        sector.NextSectorPosition++;
                    }

                    // Add to index
                    if (sector.ChunkId == 0)
                    {
                        var pointer = new IndexPointer
                        {
                            FirstSectorPosition = sector.Position,
                            Filesize            = data.Length
                        };

                        // Create index file if it does not exist yet
                        if (!this._indexStreams.ContainsKey(index))
                        {
                            this._indexStreams.Add(index, File.Open(
                                                       Path.Combine(this.CacheDirectory, "main_file_cache.idx" + (int)index),
                                                       FileMode.OpenOrCreate,
                                                       FileAccess.ReadWrite));
                        }

                        var indexWriter     = new BinaryWriter(this._indexStreams[index]);
                        var pointerPosition = fileId * IndexPointer.Length;

                        // Write zeroes up to the desired position of the index stream if it is larger than its size
                        if (indexWriter.BaseStream.Length < pointerPosition)
                        {
                            indexWriter.BaseStream.Position = indexWriter.BaseStream.Length;
                            indexWriter.Write(Enumerable.Repeat((byte)0, (int)(pointerPosition - indexWriter.BaseStream.Length)).ToArray());
                        }
                        else
                        {
                            indexWriter.BaseStream.Position = pointerPosition;
                        }

                        pointer.Encode(indexWriter.BaseStream);
                    }

                    // Write the encoded sector
                    dataWriter.BaseStream.Position = sector.Position * Sector.Length;
                    dataWriter.Write(sector.Encode());
                }
            }
        }
Example #2
0
        private IEnumerable <Sector> ReadSectors(Index index, int fileId, out int filesize)
        {
            if (!this._indexStreams.ContainsKey(index))
            {
                throw new FileNotFoundException($"Index does not exist for {(int)index}/{fileId}.");
            }

            var indexReader = new BinaryReader(this._indexStreams[index]);

            var indexPosition = (long)fileId * IndexPointer.Length;

            if (indexPosition < 0 || indexPosition >= indexReader.BaseStream.Length)
            {
                throw new FileNotFoundException($"{(int)index}/{fileId} is outside of the index file's bounds.");
            }

            var sectors = new List <Sector>();

            // Lock stream, to allow multiple threads from calling this method at the same time
            lock (this._ioLock)
            {
                indexReader.BaseStream.Position = indexPosition;
                var indexPointer = IndexPointer.Decode(indexReader.BaseStream);

                filesize = indexPointer.Filesize;

                if (indexPointer.Filesize <= 0)
                {
                    throw new FileNotFoundException($"{index}/{fileId} has no size, which means it is not stored in the cache.");
                }

                var chunkId      = 0;
                var remaining    = indexPointer.Filesize;
                var dataReader   = new BinaryReader(this._dataStream);
                var dataPosition = (long)indexPointer.FirstSectorPosition * Sector.Length;

                do
                {
                    dataReader.BaseStream.Position = dataPosition;

                    var sectorBytes = dataReader.ReadBytes(Sector.Length);

                    if (sectorBytes.Length != Sector.Length)
                    {
                        throw new EndOfStreamException($"One of {index}/{fileId}'s sectors could not be fully read.");
                    }

                    var sector = new Sector((int)(dataPosition / Sector.Length), index, fileId, chunkId++, sectorBytes);

                    var bytesRead = Math.Min(sector.Data.Length, remaining);

                    remaining -= bytesRead;

                    dataPosition = (long)sector.NextSectorPosition * Sector.Length;

                    sectors.Add(sector);
                }while (remaining > 0);
            }

            return(sectors);
        }