mirror of
https://github.com/ppy/osu
synced 2024-12-13 18:37:04 +00:00
86588778b1
After the preparatory introduction of LineBufferedReader, it is now possible to introduce registration of fallback decoders that won't drop input supplied in the first line of the file. A fallback decoder is used when the magic in the first line of the file does not match any of the other known decoders. In such a case, the fallback decoder is constructed and provided a LineBufferedReader instance. The process of matching magic only peeks the first non-empty line, so it is available for re-reading in Decode() using ReadLine(). There can be only one fallback decoder per type; a second attempt of registering a fallback will result in an exception to avoid bugs. To address the issue of parsing failing on badly or non-headered files, set the legacy decoders for Beatmaps and Storyboards as the fallbacks. Due to non-trivial logic, several new, passing unit tests with possible edge cases also included.
73 lines
2.3 KiB
C#
73 lines
2.3 KiB
C#
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
// See the LICENCE file in the repository root for full licence text.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Text;
|
|
|
|
namespace osu.Game.IO
|
|
{
|
|
/// <summary>
|
|
/// A <see cref="StreamReader"/>-like decorator (with more limited API) for <see cref="Stream"/>s
|
|
/// that allows lines to be peeked without consuming.
|
|
/// </summary>
|
|
public class LineBufferedReader : IDisposable
|
|
{
|
|
private readonly StreamReader streamReader;
|
|
private readonly Queue<string> lineBuffer;
|
|
|
|
public LineBufferedReader(Stream stream)
|
|
{
|
|
streamReader = new StreamReader(stream);
|
|
lineBuffer = new Queue<string>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reads the next line from the stream without consuming it.
|
|
/// Subsequent calls to <see cref="PeekLine"/> without a <see cref="ReadLine"/> will return the same string.
|
|
/// </summary>
|
|
public string PeekLine()
|
|
{
|
|
if (lineBuffer.Count > 0)
|
|
return lineBuffer.Peek();
|
|
|
|
var line = streamReader.ReadLine();
|
|
if (line != null)
|
|
lineBuffer.Enqueue(line);
|
|
return line;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reads the next line from the stream and consumes it.
|
|
/// If a line was peeked, that same line will then be consumed and returned.
|
|
/// </summary>
|
|
public string ReadLine() => lineBuffer.Count > 0 ? lineBuffer.Dequeue() : streamReader.ReadLine();
|
|
|
|
/// <summary>
|
|
/// Reads the stream to its end and returns the text read.
|
|
/// This includes any peeked but unconsumed lines.
|
|
/// </summary>
|
|
public string ReadToEnd()
|
|
{
|
|
var remainingText = streamReader.ReadToEnd();
|
|
if (lineBuffer.Count == 0)
|
|
return remainingText;
|
|
|
|
var builder = new StringBuilder();
|
|
|
|
// this might not be completely correct due to varying platform line endings
|
|
while (lineBuffer.Count > 0)
|
|
builder.AppendLine(lineBuffer.Dequeue());
|
|
builder.Append(remainingText);
|
|
|
|
return builder.ToString();
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
streamReader?.Dispose();
|
|
}
|
|
}
|
|
}
|