mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 00:42:44 +00:00 
			
		
		
		
	LibAudio: Switch LoaderPlugin to a more traditional constructor pattern
This now prepares all the needed (fallible) components before actually constructing a LoaderPlugin object, so we are no longer filling them in at an arbitrary later point in time.
This commit is contained in:
		
							parent
							
								
									3cf93d0dd2
								
							
						
					
					
						commit
						c57be0f474
					
				
					 12 changed files with 132 additions and 86 deletions
				
			
		|  | @ -11,10 +11,12 @@ | |||
| extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size) | ||||
| { | ||||
|     auto flac_data = ByteBuffer::copy(data, size).release_value(); | ||||
|     auto flac = make<Audio::FlacLoaderPlugin>(flac_data.bytes()); | ||||
|     auto flac_or_error = Audio::FlacLoaderPlugin::try_create(flac_data.bytes()); | ||||
| 
 | ||||
|     if (flac->initialize().is_error()) | ||||
|         return 1; | ||||
|     if (flac_or_error.is_error()) | ||||
|         return 0; | ||||
| 
 | ||||
|     auto flac = flac_or_error.release_value(); | ||||
| 
 | ||||
|     for (;;) { | ||||
|         auto samples = flac->get_more_samples(); | ||||
|  |  | |||
|  | @ -11,10 +11,12 @@ | |||
| extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size) | ||||
| { | ||||
|     auto flac_data = ByteBuffer::copy(data, size).release_value(); | ||||
|     auto mp3 = make<Audio::MP3LoaderPlugin>(flac_data.bytes()); | ||||
|     auto mp3_or_error = Audio::MP3LoaderPlugin::try_create(flac_data.bytes()); | ||||
| 
 | ||||
|     if (mp3->initialize().is_error()) | ||||
|         return 1; | ||||
|     if (mp3_or_error.is_error()) | ||||
|         return 0; | ||||
| 
 | ||||
|     auto mp3 = mp3_or_error.release_value(); | ||||
| 
 | ||||
|     for (;;) { | ||||
|         auto samples = mp3->get_more_samples(); | ||||
|  |  | |||
|  | @ -13,7 +13,12 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size) | |||
|     if (!data) | ||||
|         return 0; | ||||
|     auto wav_data = ReadonlyBytes { data, size }; | ||||
|     auto wav = make<Audio::WavLoaderPlugin>(wav_data); | ||||
|     auto wav_or_error = Audio::WavLoaderPlugin::try_create(wav_data); | ||||
| 
 | ||||
|     if (wav_or_error.is_error()) | ||||
|         return 0; | ||||
| 
 | ||||
|     auto wav = wav_or_error.release_value(); | ||||
| 
 | ||||
|     for (;;) { | ||||
|         auto samples = wav->get_more_samples(); | ||||
|  |  | |||
|  | @ -21,14 +21,16 @@ struct FlacTest : Test::TestCase { | |||
| 
 | ||||
|     void run() const | ||||
|     { | ||||
|         auto loader = Audio::FlacLoaderPlugin { m_path.string() }; | ||||
|         if (auto result = loader.initialize(); result.is_error()) { | ||||
|         auto result = Audio::FlacLoaderPlugin::try_create(m_path.string()); | ||||
|         if (result.is_error()) { | ||||
|             FAIL(String::formatted("{} (at {})", result.error().description, result.error().index)); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         auto loader = result.release_value(); | ||||
| 
 | ||||
|         while (true) { | ||||
|             auto maybe_samples = loader.get_more_samples(2 * MiB); | ||||
|             auto maybe_samples = loader->get_more_samples(2 * MiB); | ||||
|             if (maybe_samples.is_error()) { | ||||
|                 FAIL(String::formatted("{} (at {})", maybe_samples.error().description, maybe_samples.error().index)); | ||||
|                 return; | ||||
|  |  | |||
|  | @ -25,20 +25,33 @@ | |||
| 
 | ||||
| namespace Audio { | ||||
| 
 | ||||
| FlacLoaderPlugin::FlacLoaderPlugin(StringView path) | ||||
|     : LoaderPlugin(path) | ||||
| FlacLoaderPlugin::FlacLoaderPlugin(OwnPtr<Core::Stream::SeekableStream> stream) | ||||
|     : LoaderPlugin(move(stream)) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| FlacLoaderPlugin::FlacLoaderPlugin(Bytes buffer) | ||||
|     : LoaderPlugin(buffer) | ||||
| Result<NonnullOwnPtr<FlacLoaderPlugin>, LoaderError> FlacLoaderPlugin::try_create(StringView path) | ||||
| { | ||||
|     auto stream = LOADER_TRY(Core::Stream::BufferedFile::create(LOADER_TRY(Core::Stream::File::open(path, Core::Stream::OpenMode::Read)))); | ||||
|     auto loader = make<FlacLoaderPlugin>(move(stream)); | ||||
| 
 | ||||
|     LOADER_TRY(loader->initialize()); | ||||
| 
 | ||||
|     return loader; | ||||
| } | ||||
| 
 | ||||
| Result<NonnullOwnPtr<FlacLoaderPlugin>, LoaderError> FlacLoaderPlugin::try_create(Bytes buffer) | ||||
| { | ||||
|     auto stream = LOADER_TRY(Core::Stream::MemoryStream::construct(buffer)); | ||||
|     auto loader = make<FlacLoaderPlugin>(move(stream)); | ||||
| 
 | ||||
|     LOADER_TRY(loader->initialize()); | ||||
| 
 | ||||
|     return loader; | ||||
| } | ||||
| 
 | ||||
| MaybeLoaderError FlacLoaderPlugin::initialize() | ||||
| { | ||||
|     LOADER_TRY(LoaderPlugin::initialize()); | ||||
| 
 | ||||
|     TRY(parse_header()); | ||||
|     TRY(reset()); | ||||
|     return {}; | ||||
|  |  | |||
|  | @ -47,11 +47,11 @@ ALWAYS_INLINE ErrorOr<i32> decode_unsigned_exp_golomb(u8 order, BigEndianInputBi | |||
| //      https://datatracker.ietf.org/doc/html/draft-ietf-cellar-flac-03 (newer IETF draft that uses incompatible numberings and names)
 | ||||
| class FlacLoaderPlugin : public LoaderPlugin { | ||||
| public: | ||||
|     explicit FlacLoaderPlugin(StringView path); | ||||
|     explicit FlacLoaderPlugin(Bytes buffer); | ||||
|     explicit FlacLoaderPlugin(OwnPtr<Core::Stream::SeekableStream> stream); | ||||
|     virtual ~FlacLoaderPlugin() override = default; | ||||
| 
 | ||||
|     virtual MaybeLoaderError initialize() override; | ||||
|     static Result<NonnullOwnPtr<FlacLoaderPlugin>, LoaderError> try_create(StringView path); | ||||
|     static Result<NonnullOwnPtr<FlacLoaderPlugin>, LoaderError> try_create(Bytes buffer); | ||||
| 
 | ||||
|     virtual LoaderSamples get_more_samples(size_t max_bytes_to_read_from_input = 128 * KiB) override; | ||||
| 
 | ||||
|  | @ -69,6 +69,7 @@ public: | |||
|     bool sample_count_unknown() const { return m_total_samples == 0; } | ||||
| 
 | ||||
| private: | ||||
|     MaybeLoaderError initialize(); | ||||
|     MaybeLoaderError parse_header(); | ||||
|     // Either returns the metadata block or sets error message.
 | ||||
|     // Additionally, increments m_data_start_location past the read meta block.
 | ||||
|  |  | |||
|  | @ -11,26 +11,11 @@ | |||
| 
 | ||||
| namespace Audio { | ||||
| 
 | ||||
| LoaderPlugin::LoaderPlugin(StringView path) | ||||
|     : m_path(path) | ||||
| LoaderPlugin::LoaderPlugin(OwnPtr<Core::Stream::SeekableStream> stream) | ||||
|     : m_stream(move(stream)) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| LoaderPlugin::LoaderPlugin(Bytes buffer) | ||||
|     : m_backing_memory(buffer) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| MaybeLoaderError LoaderPlugin::initialize() | ||||
| { | ||||
|     if (m_backing_memory.has_value()) | ||||
|         m_stream = LOADER_TRY(Core::Stream::MemoryStream::construct(m_backing_memory.value())); | ||||
|     else | ||||
|         m_stream = LOADER_TRY(Core::Stream::BufferedFile::create(LOADER_TRY(Core::Stream::File::open(m_path, Core::Stream::OpenMode::Read)))); | ||||
| 
 | ||||
|     return {}; | ||||
| } | ||||
| 
 | ||||
| Loader::Loader(NonnullOwnPtr<LoaderPlugin> plugin) | ||||
|     : m_plugin(move(plugin)) | ||||
| { | ||||
|  | @ -38,35 +23,47 @@ Loader::Loader(NonnullOwnPtr<LoaderPlugin> plugin) | |||
| 
 | ||||
| Result<NonnullOwnPtr<LoaderPlugin>, LoaderError> Loader::try_create(StringView path) | ||||
| { | ||||
|     NonnullOwnPtr<LoaderPlugin> plugin = adopt_own(*new WavLoaderPlugin(path)); | ||||
|     auto initstate0 = plugin->initialize(); | ||||
|     if (!initstate0.is_error()) | ||||
|         return plugin; | ||||
|     { | ||||
|         auto plugin = WavLoaderPlugin::try_create(path); | ||||
|         if (!plugin.is_error()) | ||||
|             return NonnullOwnPtr<LoaderPlugin>(plugin.release_value()); | ||||
|     } | ||||
| 
 | ||||
|     plugin = adopt_own(*new FlacLoaderPlugin(path)); | ||||
|     auto initstate1 = plugin->initialize(); | ||||
|     if (!initstate1.is_error()) | ||||
|         return plugin; | ||||
|     { | ||||
|         auto plugin = FlacLoaderPlugin::try_create(path); | ||||
|         if (!plugin.is_error()) | ||||
|             return NonnullOwnPtr<LoaderPlugin>(plugin.release_value()); | ||||
|     } | ||||
| 
 | ||||
|     plugin = adopt_own(*new MP3LoaderPlugin(path)); | ||||
|     auto initstate2 = plugin->initialize(); | ||||
|     if (!initstate2.is_error()) | ||||
|         return plugin; | ||||
|     { | ||||
|         auto plugin = MP3LoaderPlugin::try_create(path); | ||||
|         if (!plugin.is_error()) | ||||
|             return NonnullOwnPtr<LoaderPlugin>(plugin.release_value()); | ||||
|     } | ||||
| 
 | ||||
|     return LoaderError { "No loader plugin available" }; | ||||
| } | ||||
| 
 | ||||
| Result<NonnullOwnPtr<LoaderPlugin>, LoaderError> Loader::try_create(Bytes buffer) | ||||
| { | ||||
|     NonnullOwnPtr<LoaderPlugin> plugin = adopt_own(*new WavLoaderPlugin(buffer)); | ||||
|     if (auto initstate = plugin->initialize(); !initstate.is_error()) | ||||
|         return plugin; | ||||
|     plugin = adopt_own(*new FlacLoaderPlugin(buffer)); | ||||
|     if (auto initstate = plugin->initialize(); !initstate.is_error()) | ||||
|         return plugin; | ||||
|     plugin = adopt_own(*new MP3LoaderPlugin(buffer)); | ||||
|     if (auto initstate = plugin->initialize(); !initstate.is_error()) | ||||
|         return plugin; | ||||
|     { | ||||
|         auto plugin = WavLoaderPlugin::try_create(buffer); | ||||
|         if (!plugin.is_error()) | ||||
|             return NonnullOwnPtr<LoaderPlugin>(plugin.release_value()); | ||||
|     } | ||||
| 
 | ||||
|     { | ||||
|         auto plugin = FlacLoaderPlugin::try_create(buffer); | ||||
|         if (!plugin.is_error()) | ||||
|             return NonnullOwnPtr<LoaderPlugin>(plugin.release_value()); | ||||
|     } | ||||
| 
 | ||||
|     { | ||||
|         auto plugin = MP3LoaderPlugin::try_create(buffer); | ||||
|         if (!plugin.is_error()) | ||||
|             return NonnullOwnPtr<LoaderPlugin>(plugin.release_value()); | ||||
|     } | ||||
| 
 | ||||
|     return LoaderError { "No loader plugin available" }; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -30,12 +30,9 @@ using MaybeLoaderError = Result<void, LoaderError>; | |||
| 
 | ||||
| class LoaderPlugin { | ||||
| public: | ||||
|     explicit LoaderPlugin(StringView path); | ||||
|     explicit LoaderPlugin(Bytes buffer); | ||||
|     explicit LoaderPlugin(OwnPtr<Core::Stream::SeekableStream> stream); | ||||
|     virtual ~LoaderPlugin() = default; | ||||
| 
 | ||||
|     virtual MaybeLoaderError initialize() = 0; | ||||
| 
 | ||||
|     virtual LoaderSamples get_more_samples(size_t max_bytes_to_read_from_input = 128 * KiB) = 0; | ||||
| 
 | ||||
|     virtual MaybeLoaderError reset() = 0; | ||||
|  | @ -61,10 +58,7 @@ public: | |||
|     Vector<PictureData> const& pictures() const { return m_pictures; }; | ||||
| 
 | ||||
| protected: | ||||
|     StringView m_path; | ||||
|     OwnPtr<Core::Stream::SeekableStream> m_stream; | ||||
|     // The constructor might set this so that we can initialize the data stream later.
 | ||||
|     Optional<Bytes> m_backing_memory; | ||||
| 
 | ||||
|     Vector<PictureData> m_pictures; | ||||
| }; | ||||
|  |  | |||
|  | @ -14,20 +14,33 @@ namespace Audio { | |||
| DSP::MDCT<12> MP3LoaderPlugin::s_mdct_12; | ||||
| DSP::MDCT<36> MP3LoaderPlugin::s_mdct_36; | ||||
| 
 | ||||
| MP3LoaderPlugin::MP3LoaderPlugin(StringView path) | ||||
|     : LoaderPlugin(path) | ||||
| MP3LoaderPlugin::MP3LoaderPlugin(OwnPtr<Core::Stream::SeekableStream> stream) | ||||
|     : LoaderPlugin(move(stream)) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| MP3LoaderPlugin::MP3LoaderPlugin(Bytes buffer) | ||||
|     : LoaderPlugin(buffer) | ||||
| Result<NonnullOwnPtr<MP3LoaderPlugin>, LoaderError> MP3LoaderPlugin::try_create(StringView path) | ||||
| { | ||||
|     auto stream = LOADER_TRY(Core::Stream::BufferedFile::create(LOADER_TRY(Core::Stream::File::open(path, Core::Stream::OpenMode::Read)))); | ||||
|     auto loader = make<MP3LoaderPlugin>(move(stream)); | ||||
| 
 | ||||
|     LOADER_TRY(loader->initialize()); | ||||
| 
 | ||||
|     return loader; | ||||
| } | ||||
| 
 | ||||
| Result<NonnullOwnPtr<MP3LoaderPlugin>, LoaderError> MP3LoaderPlugin::try_create(Bytes buffer) | ||||
| { | ||||
|     auto stream = LOADER_TRY(Core::Stream::MemoryStream::construct(buffer)); | ||||
|     auto loader = make<MP3LoaderPlugin>(move(stream)); | ||||
| 
 | ||||
|     LOADER_TRY(loader->initialize()); | ||||
| 
 | ||||
|     return loader; | ||||
| } | ||||
| 
 | ||||
| MaybeLoaderError MP3LoaderPlugin::initialize() | ||||
| { | ||||
|     LOADER_TRY(LoaderPlugin::initialize()); | ||||
| 
 | ||||
|     m_bitstream = LOADER_TRY(Core::Stream::BigEndianInputBitStream::construct(*m_stream)); | ||||
| 
 | ||||
|     TRY(synchronize()); | ||||
|  |  | |||
|  | @ -23,11 +23,12 @@ struct ScaleFactorBand; | |||
| 
 | ||||
| class MP3LoaderPlugin : public LoaderPlugin { | ||||
| public: | ||||
|     explicit MP3LoaderPlugin(StringView path); | ||||
|     explicit MP3LoaderPlugin(Bytes buffer); | ||||
|     explicit MP3LoaderPlugin(OwnPtr<Core::Stream::SeekableStream> stream); | ||||
|     virtual ~MP3LoaderPlugin() = default; | ||||
| 
 | ||||
|     virtual MaybeLoaderError initialize() override; | ||||
|     static Result<NonnullOwnPtr<MP3LoaderPlugin>, LoaderError> try_create(StringView path); | ||||
|     static Result<NonnullOwnPtr<MP3LoaderPlugin>, LoaderError> try_create(Bytes buffer); | ||||
| 
 | ||||
|     virtual LoaderSamples get_more_samples(size_t max_bytes_to_read_from_input = 128 * KiB) override; | ||||
| 
 | ||||
|     virtual MaybeLoaderError reset() override; | ||||
|  | @ -41,6 +42,7 @@ public: | |||
|     virtual String format_name() override { return "MP3 (.mp3)"; } | ||||
| 
 | ||||
| private: | ||||
|     MaybeLoaderError initialize(); | ||||
|     MaybeLoaderError synchronize(); | ||||
|     MaybeLoaderError build_seek_table(); | ||||
|     ErrorOr<MP3::Header, LoaderError> read_header(); | ||||
|  |  | |||
|  | @ -18,24 +18,38 @@ namespace Audio { | |||
| 
 | ||||
| static constexpr size_t const maximum_wav_size = 1 * GiB; // FIXME: is there a more appropriate size limit?
 | ||||
| 
 | ||||
| WavLoaderPlugin::WavLoaderPlugin(StringView path) | ||||
|     : LoaderPlugin(path) | ||||
| WavLoaderPlugin::WavLoaderPlugin(OwnPtr<Core::Stream::SeekableStream> stream) | ||||
|     : LoaderPlugin(move(stream)) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| Result<NonnullOwnPtr<WavLoaderPlugin>, LoaderError> WavLoaderPlugin::try_create(StringView path) | ||||
| { | ||||
|     auto stream = LOADER_TRY(Core::Stream::BufferedFile::create(LOADER_TRY(Core::Stream::File::open(path, Core::Stream::OpenMode::Read)))); | ||||
|     auto loader = make<WavLoaderPlugin>(move(stream)); | ||||
| 
 | ||||
|     LOADER_TRY(loader->initialize()); | ||||
| 
 | ||||
|     return loader; | ||||
| } | ||||
| 
 | ||||
| Result<NonnullOwnPtr<WavLoaderPlugin>, LoaderError> WavLoaderPlugin::try_create(Bytes buffer) | ||||
| { | ||||
|     auto stream = LOADER_TRY(Core::Stream::MemoryStream::construct(buffer)); | ||||
|     auto loader = make<WavLoaderPlugin>(move(stream)); | ||||
| 
 | ||||
|     LOADER_TRY(loader->initialize()); | ||||
| 
 | ||||
|     return loader; | ||||
| } | ||||
| 
 | ||||
| MaybeLoaderError WavLoaderPlugin::initialize() | ||||
| { | ||||
|     LOADER_TRY(LoaderPlugin::initialize()); | ||||
|     LOADER_TRY(parse_header()); | ||||
| 
 | ||||
|     TRY(parse_header()); | ||||
|     return {}; | ||||
| } | ||||
| 
 | ||||
| WavLoaderPlugin::WavLoaderPlugin(Bytes buffer) | ||||
|     : LoaderPlugin(buffer) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| template<typename SampleReader> | ||||
| MaybeLoaderError WavLoaderPlugin::read_samples_from_stream(Core::Stream::Stream& stream, SampleReader read_sample, FixedArray<Sample>& samples) const | ||||
| { | ||||
|  |  | |||
|  | @ -29,10 +29,9 @@ static constexpr unsigned const WAVE_FORMAT_EXTENSIBLE = 0xFFFE; // Determined b | |||
| // Parses and reads audio data from a WAV file.
 | ||||
| class WavLoaderPlugin : public LoaderPlugin { | ||||
| public: | ||||
|     explicit WavLoaderPlugin(StringView path); | ||||
|     explicit WavLoaderPlugin(Bytes buffer); | ||||
| 
 | ||||
|     virtual MaybeLoaderError initialize() override; | ||||
|     explicit WavLoaderPlugin(OwnPtr<Core::Stream::SeekableStream> stream); | ||||
|     static Result<NonnullOwnPtr<WavLoaderPlugin>, LoaderError> try_create(StringView path); | ||||
|     static Result<NonnullOwnPtr<WavLoaderPlugin>, LoaderError> try_create(Bytes buffer); | ||||
| 
 | ||||
|     virtual LoaderSamples get_more_samples(size_t max_samples_to_read_from_input = 128 * KiB) override; | ||||
| 
 | ||||
|  | @ -50,6 +49,8 @@ public: | |||
|     virtual PcmSampleFormat pcm_format() override { return m_sample_format; } | ||||
| 
 | ||||
| private: | ||||
|     MaybeLoaderError initialize(); | ||||
| 
 | ||||
|     MaybeLoaderError parse_header(); | ||||
| 
 | ||||
|     LoaderSamples samples_from_pcm_data(Bytes const& data, size_t samples_to_read) const; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Tim Schumacher
						Tim Schumacher