Exemple #1
0
        /// <summary>
        /// Reads lines from the current position to the end of the file.
        /// </summary>
        private static async Task <CacheRetrievalResult> RetrieveInternalAsync(FileStream fs)
        {
            List <Scrobble> result      = new List <Scrobble>();
            List <string>   failedLines = new List <string>();

            using (var reader = GetReader(fs))
            {
                while (!reader.EndOfStream)
                {
                    // Try to parse each line to a scrobble, ignoring failures.
                    var line = await reader.ReadLineAsync();

                    if (!string.IsNullOrWhiteSpace(line))
                    {
                        try
                        {
                            var scrobble = ScrobbleSerializer.Deserialize(line);
                            result.Add(scrobble);
                        }
                        catch
                        {
                            failedLines.Add(line);
                        }
                    }
                }
            }
            return(new CacheRetrievalResult(result, failedLines));
        }
Exemple #2
0
        /// <summary>
        /// Asynchronously stores a scrobble in the underlying cache file.
        /// Will throw an exception if the cache file cannot be accessed.
        /// This method is thread safe.
        /// </summary>
        public async Task StoreAsync(Scrobble scrobble)
        {
            EnsureFileLockIsAcquired();

            using (await _fileOperationAsyncLock.LockAsync())
            {
                // Note about the file content:
                // The file is line separated.
                // Instead of writing the data followed by a new line, we do the opposite:
                // We always start by writing a new line, followed by the data.
                // This way, we don't risk continuing a corrupted line without a proper line ending.
                // Unreadable lines can then be discarded, and we lose only one record instead of two.

                await EnsureCorrectHeaderAndGetFileVersionAsync(_fileStream);

                // Skip to the end of the file to append a new scrobble.
                _fileStream.Seek(0, SeekOrigin.End);

                var    serializedScrobble = ScrobbleSerializer.Serialize(scrobble);
                byte[] buffer             = Encoding.UTF8.GetBytes(serializedScrobble);
                byte[] newLineBuffer      = Encoding.UTF8.GetBytes("\n");
                await _fileStream.WriteAsync(newLineBuffer, 0, newLineBuffer.Length);

                await _fileStream.WriteAsync(buffer, 0, buffer.Length);
            }
        }
Exemple #3
0
        /// <summary>
        /// Erases the file, write a new header, then write the scrobbles.
        /// </summary>
        private static async Task OverwriteFileAsync(FileStream fs, IEnumerable <Scrobble> scrobbles)
        {
            // Truncate the file.
            fs.SetLength(0);
            // Write the header.
            await EnsureCorrectHeaderAndGetFileVersionAsync(fs);

            // Skip to the end of the file to append the scrobbles.
            fs.Seek(0, SeekOrigin.End);

            // To minimize file writes, we use a StringBuilder that will be written in the file
            // in as few buffer lengths as possible.
            StringBuilder sb = new StringBuilder();

            foreach (var scrobble in scrobbles)
            {
                var serialized = ScrobbleSerializer.Serialize(scrobble);
                sb.AppendLine();
                sb.Append(serialized);
            }

            byte[] buffer = Encoding.UTF8.GetBytes(sb.ToString());
            await fs.WriteAsync(buffer, 0, buffer.Length);
        }