Пример #1
0
        /// <summary>
        ///     Gets the stream of a cache entry.
        ///     DO NOT CLOSE THE STREAM RETURNED BY THIS METHOD.
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public StreamEx GetEntry(String name)
        {
            logger.Debug("Getting cache entry: ID = 0x{0}", name);

            // Check if there is such an entry to get
            if (!entriesMap.ContainsKey(name))
            {
                logger.Error("Attempting to get an entry that does not exist: ID = 0x{0}", name);
                throw new KeyNotFoundException();
            }

            // Get the entry
            CacheEntry      metadata = entries[entriesMap[name]];
            PartialStreamEx stream   = new PartialStreamEx(dataStream, metadata.Address, metadata.Length);

            return(stream);
        }
Пример #2
0
        /// <summary>
        ///     Adds data into cache.
        ///     In case of a duplicate entry, the old entry will be overwritten.
        /// </summary>
        /// <param name="name">Name of the entry.</param>
        /// <param name="data">Stream containing data to be cached.</param>
        public void AddEntry(String name, StreamEx data)
        {
            logger.Debug("Adding cache entry: ID = {0}; Length = {1}", name, data.Length);

            // If the entry name is too long, throw error
            if (Encoding.UTF8.GetByteCount(name) > (IndexAlignment - 0x10))
            {
                throw new ArgumentOutOfRangeException("name",
                                                      String.Format("Name too long! Name must be less than {0} bytes long when encoded.",
                                                                    IndexAlignment - 0x10));
            }

            // If the entry already exists, delete it
            if (entriesMap.ContainsKey(name))
            {
                RemoveEntry(name);
                logger.Trace("Attempting to add a duplicate entry, removing old data.");
            }

            // Find a hole to write the data
            Int64 address;
            KeyValuePair <long, long> hole = dataHoles.FirstOrDefault(t => t.Value >= data.Length);

            if (!hole.Equals(default(KeyValuePair <Int64, Int64>)))
            {
                logger.Trace("Hole found: Address = 0x{0}; Length = 0x{1}", hole.Key.ToHexString(8),
                             hole.Value.ToHexString(8));

                // Appropriate hole found, write data first
                address = hole.Key;
                // Use partial stream to avoid overflows
                PartialStreamEx partialStream = new PartialStreamEx(dataStream, address, hole.Value);
                data.CopyTo(partialStream);

                // Reduce hole size
                dataHoles.Remove(hole.Key);
                long newHoleAddress = Align(hole.Key + data.Length, DataAlignment);
                long newHoleLength  = hole.Value - Align(data.Length, DataAlignment);

                Debug.Assert(newHoleLength > 0, "Hole size should be greater or equal than zero after resize.");
                Debug.Assert(newHoleAddress % DataAlignment == 0, "New hole address should be aligned.");
                Debug.Assert(newHoleLength % DataAlignment == 0, "New hole length should be aligned.");

                // Add it back if there is still hole after the write
                if (newHoleLength > DataAlignment)
                {
                    logger.Trace("Hole reduced to: Address = 0x{0}; Length = 0x{1}", newHoleAddress.ToHexString(8),
                                 newHoleLength.ToHexString(8));
                    dataHoles.Add(newHoleAddress, newHoleLength);
                }
            }
            else
            {
                logger.Trace("No holes large enough, writing to the end of data file.");
                address = dataStream.Seek(0, SeekOrigin.End);

                // Write Data
                data.CopyTo(dataStream);

                // Pad for alignment
                long fill = Align(dataStream.Position, DataAlignment) - dataStream.Position;
                dataStream.WriteBytes(new byte[fill]);

                Debug.Assert(dataStream.Position % DataAlignment == 0,
                             "Data stream position should be aligned after write.");
            }

            // Find an empty slot in the index file
            long indexAddress = indexStream.Seek(0, SeekOrigin.End); // Set it to end of file first

            if (indexHoles.Count > 0)
            {
                indexAddress = indexHoles.Dequeue(); // If there are empty index slots, use those instead
                logger.Trace("Found an empty index slot at 0x{0}", indexAddress.ToHexString(8));
            }

            Debug.Assert(indexAddress % 0x10 == 0, "Index address should be aligned to 0x10.");

            // Write the index
            indexStream.Position = indexAddress;
            indexStream.WriteInt64(address);
            indexStream.WriteInt64(data.Length);
            byte[] encodedName = Encoding.UTF8.GetBytes(name);
            indexStream.WriteBytes(encodedName);
            indexStream.WriteBytes(new Byte[IndexAlignment - 0x10 - encodedName.Length]); // Padding

            // Save the index
            entries.Add(address, new CacheEntry
            {
                Name            = name,
                Address         = address,
                Length          = data.Length,
                MetadataAddress = indexAddress
            });
            entriesMap.Add(name, address);

            // Force write data to stream
            indexStream.Flush();
            dataStream.Flush();
        }