1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 14:17:36 +00:00

LibGfx: GIFLoader: Propagate more errors

Migrate bool and Optional<> result types
to ErrorOr<>
This commit is contained in:
ericLemanissier 2022-12-23 13:36:19 +01:00 committed by Tim Flynn
parent 05b9e6ac3b
commit 49f697ed56

View file

@ -88,7 +88,7 @@ enum class GIFFormat {
GIF89a, GIF89a,
}; };
static Optional<GIFFormat> decode_gif_header(InputMemoryStream& stream) static ErrorOr<GIFFormat> decode_gif_header(InputMemoryStream& stream)
{ {
static auto valid_header_87 = "GIF87a"sv; static auto valid_header_87 = "GIF87a"sv;
static auto valid_header_89 = "GIF89a"sv; static auto valid_header_89 = "GIF89a"sv;
@ -96,15 +96,14 @@ static Optional<GIFFormat> decode_gif_header(InputMemoryStream& stream)
Array<u8, 6> header; Array<u8, 6> header;
stream >> header; stream >> header;
if (stream.handle_any_error()) TRY(stream.try_handle_any_error());
return {};
if (header.span() == valid_header_87.bytes()) if (header.span() == valid_header_87.bytes())
return GIFFormat::GIF87a; return GIFFormat::GIF87a;
if (header.span() == valid_header_89.bytes()) if (header.span() == valid_header_89.bytes())
return GIFFormat::GIF89a; return GIFFormat::GIF89a;
return {}; return Error::from_string_literal("GIF header unknown");
} }
class LZWDecoder { class LZWDecoder {
@ -144,11 +143,11 @@ public:
m_output.clear(); m_output.clear();
} }
Optional<u16> next_code() ErrorOr<u16> next_code()
{ {
size_t current_byte_index = m_current_bit_index / 8; size_t current_byte_index = m_current_bit_index / 8;
if (current_byte_index >= m_lzw_bytes.size()) { if (current_byte_index >= m_lzw_bytes.size()) {
return {}; return Error::from_string_literal("LZWDecoder tries to read ouf of bounds");
} }
// Extract the code bits using a 32-bit mask to cover the possibility that if // Extract the code bits using a 32-bit mask to cover the possibility that if
@ -175,13 +174,13 @@ public:
m_current_code, m_current_code,
m_current_bit_index, m_current_bit_index,
m_code_table.size()); m_code_table.size());
return {}; return Error::from_string_literal("Corrupted LZW stream, invalid code");
} else if (m_current_code == m_code_table.size() && m_output.is_empty()) { } else if (m_current_code == m_code_table.size() && m_output.is_empty()) {
dbgln_if(GIF_DEBUG, "Corrupted LZW stream, valid new code but output buffer is empty: {} at bit index {}, code table size: {}", dbgln_if(GIF_DEBUG, "Corrupted LZW stream, valid new code but output buffer is empty: {} at bit index {}, code table size: {}",
m_current_code, m_current_code,
m_current_bit_index, m_current_bit_index,
m_code_table.size()); m_code_table.size());
return {}; return Error::from_string_literal("Corrupted LZW stream, valid new code but output buffer is empty");
} }
m_current_bit_index += m_code_size; m_current_bit_index += m_code_size;
@ -326,10 +325,10 @@ static ErrorOr<void> decode_frame(GIFLoadingContext& context, size_t frame_index
int row = 0; int row = 0;
int interlace_pass = 0; int interlace_pass = 0;
while (true) { while (true) {
Optional<u16> code = decoder.next_code(); ErrorOr<u16> code = decoder.next_code();
if (!code.has_value()) { if (code.is_error()) {
dbgln_if(GIF_DEBUG, "Unexpectedly reached end of gif frame data"); dbgln_if(GIF_DEBUG, "Unexpectedly reached end of gif frame data");
return Error::from_string_literal("Unexpectedly reached end of gif frame data"); return code.release_error();
} }
if (code.value() == clear_code) { if (code.value() == clear_code) {
@ -378,17 +377,14 @@ static ErrorOr<void> decode_frame(GIFLoadingContext& context, size_t frame_index
return {}; return {};
} }
static bool load_gif_frame_descriptors(GIFLoadingContext& context) static ErrorOr<void> load_gif_frame_descriptors(GIFLoadingContext& context)
{ {
if (context.data_size < 32) if (context.data_size < 32)
return false; return Error::from_string_literal("Size too short for GIF frame descriptors");
InputMemoryStream stream { { context.data, context.data_size } }; InputMemoryStream stream { { context.data, context.data_size } };
Optional<GIFFormat> format = decode_gif_header(stream); TRY(decode_gif_header(stream));
if (!format.has_value()) {
return false;
}
LittleEndian<u16> value; LittleEndian<u16> value;
@ -398,28 +394,24 @@ static bool load_gif_frame_descriptors(GIFLoadingContext& context)
stream >> value; stream >> value;
context.logical_screen.height = value; context.logical_screen.height = value;
if (stream.handle_any_error()) TRY(stream.try_handle_any_error());
return false;
if (context.logical_screen.width > maximum_width_for_decoded_images || context.logical_screen.height > maximum_height_for_decoded_images) { if (context.logical_screen.width > maximum_width_for_decoded_images || context.logical_screen.height > maximum_height_for_decoded_images) {
dbgln("This GIF is too large for comfort: {}x{}", context.logical_screen.width, context.logical_screen.height); dbgln("This GIF is too large for comfort: {}x{}", context.logical_screen.width, context.logical_screen.height);
return false; return Error::from_string_literal("This GIF is too large for comfort");
} }
u8 gcm_info = 0; u8 gcm_info = 0;
stream >> gcm_info; stream >> gcm_info;
if (stream.handle_any_error()) TRY(stream.try_handle_any_error());
return false;
stream >> context.background_color_index; stream >> context.background_color_index;
if (stream.handle_any_error()) TRY(stream.try_handle_any_error());
return false;
u8 pixel_aspect_ratio = 0; u8 pixel_aspect_ratio = 0;
stream >> pixel_aspect_ratio; stream >> pixel_aspect_ratio;
if (stream.handle_any_error()) TRY(stream.try_handle_any_error());
return false;
u8 bits_per_pixel = (gcm_info & 7) + 1; u8 bits_per_pixel = (gcm_info & 7) + 1;
int color_map_entry_count = 1; int color_map_entry_count = 1;
@ -434,22 +426,19 @@ static bool load_gif_frame_descriptors(GIFLoadingContext& context)
context.logical_screen.color_map[i] = { r, g, b }; context.logical_screen.color_map[i] = { r, g, b };
} }
if (stream.handle_any_error()) TRY(stream.try_handle_any_error());
return false;
NonnullOwnPtr<GIFImageDescriptor> current_image = make<GIFImageDescriptor>(); NonnullOwnPtr<GIFImageDescriptor> current_image = make<GIFImageDescriptor>();
for (;;) { for (;;) {
u8 sentinel = 0; u8 sentinel = 0;
stream >> sentinel; stream >> sentinel;
if (stream.handle_any_error()) TRY(stream.try_handle_any_error());
return false;
if (sentinel == '!') { if (sentinel == '!') {
u8 extension_type = 0; u8 extension_type = 0;
stream >> extension_type; stream >> extension_type;
if (stream.handle_any_error()) TRY(stream.try_handle_any_error());
return false;
u8 sub_block_length = 0; u8 sub_block_length = 0;
@ -457,8 +446,7 @@ static bool load_gif_frame_descriptors(GIFLoadingContext& context)
for (;;) { for (;;) {
stream >> sub_block_length; stream >> sub_block_length;
if (stream.handle_any_error()) TRY(stream.try_handle_any_error());
return false;
if (sub_block_length == 0) if (sub_block_length == 0)
break; break;
@ -469,8 +457,7 @@ static bool load_gif_frame_descriptors(GIFLoadingContext& context)
sub_block.append(dummy); sub_block.append(dummy);
} }
if (stream.handle_any_error()) TRY(stream.try_handle_any_error());
return false;
} }
if (extension_type == 0xF9) { if (extension_type == 0xF9) {
@ -533,8 +520,7 @@ static bool load_gif_frame_descriptors(GIFLoadingContext& context)
image.height = tmp; image.height = tmp;
stream >> packed_fields; stream >> packed_fields;
if (stream.handle_any_error()) TRY(stream.try_handle_any_error());
return false;
image.use_global_color_map = !(packed_fields & 0x80); image.use_global_color_map = !(packed_fields & 0x80);
image.interlaced = (packed_fields & 0x40) != 0; image.interlaced = (packed_fields & 0x40) != 0;
@ -552,16 +538,14 @@ static bool load_gif_frame_descriptors(GIFLoadingContext& context)
} }
stream >> image.lzw_min_code_size; stream >> image.lzw_min_code_size;
if (stream.handle_any_error()) TRY(stream.try_handle_any_error());
return false;
u8 lzw_encoded_bytes_expected = 0; u8 lzw_encoded_bytes_expected = 0;
for (;;) { for (;;) {
stream >> lzw_encoded_bytes_expected; stream >> lzw_encoded_bytes_expected;
if (stream.handle_any_error()) TRY(stream.try_handle_any_error());
return false;
if (lzw_encoded_bytes_expected == 0) if (lzw_encoded_bytes_expected == 0)
break; break;
@ -569,8 +553,7 @@ static bool load_gif_frame_descriptors(GIFLoadingContext& context)
Array<u8, 256> buffer; Array<u8, 256> buffer;
stream >> buffer.span().trim(lzw_encoded_bytes_expected); stream >> buffer.span().trim(lzw_encoded_bytes_expected);
if (stream.handle_any_error()) TRY(stream.try_handle_any_error());
return false;
for (int i = 0; i < lzw_encoded_bytes_expected; ++i) { for (int i = 0; i < lzw_encoded_bytes_expected; ++i) {
image.lzw_encoded_bytes.append(buffer[i]); image.lzw_encoded_bytes.append(buffer[i]);
@ -585,11 +568,11 @@ static bool load_gif_frame_descriptors(GIFLoadingContext& context)
break; break;
} }
return false; return Error::from_string_literal("Unexpected sentinel");
} }
context.state = GIFLoadingContext::State::FrameDescriptorsLoaded; context.state = GIFLoadingContext::State::FrameDescriptorsLoaded;
return true; return {};
} }
GIFImageDecoderPlugin::GIFImageDecoderPlugin(u8 const* data, size_t size) GIFImageDecoderPlugin::GIFImageDecoderPlugin(u8 const* data, size_t size)
@ -608,7 +591,7 @@ IntSize GIFImageDecoderPlugin::size()
} }
if (m_context->state < GIFLoadingContext::State::FrameDescriptorsLoaded) { if (m_context->state < GIFLoadingContext::State::FrameDescriptorsLoaded) {
if (!load_gif_frame_descriptors(*m_context)) { if (load_gif_frame_descriptors(*m_context).is_error()) {
m_context->error_state = GIFLoadingContext::ErrorState::FailedToLoadFrameDescriptors; m_context->error_state = GIFLoadingContext::ErrorState::FailedToLoadFrameDescriptors;
return {}; return {};
} }
@ -634,7 +617,7 @@ bool GIFImageDecoderPlugin::set_nonvolatile(bool& was_purged)
bool GIFImageDecoderPlugin::sniff() bool GIFImageDecoderPlugin::sniff()
{ {
InputMemoryStream stream { { m_context->data, m_context->data_size } }; InputMemoryStream stream { { m_context->data, m_context->data_size } };
return decode_gif_header(stream).has_value(); return !decode_gif_header(stream).is_error();
} }
bool GIFImageDecoderPlugin::is_animated() bool GIFImageDecoderPlugin::is_animated()
@ -644,7 +627,7 @@ bool GIFImageDecoderPlugin::is_animated()
} }
if (m_context->state < GIFLoadingContext::State::FrameDescriptorsLoaded) { if (m_context->state < GIFLoadingContext::State::FrameDescriptorsLoaded) {
if (!load_gif_frame_descriptors(*m_context)) { if (load_gif_frame_descriptors(*m_context).is_error()) {
m_context->error_state = GIFLoadingContext::ErrorState::FailedToLoadFrameDescriptors; m_context->error_state = GIFLoadingContext::ErrorState::FailedToLoadFrameDescriptors;
return false; return false;
} }
@ -660,7 +643,7 @@ size_t GIFImageDecoderPlugin::loop_count()
} }
if (m_context->state < GIFLoadingContext::State::FrameDescriptorsLoaded) { if (m_context->state < GIFLoadingContext::State::FrameDescriptorsLoaded) {
if (!load_gif_frame_descriptors(*m_context)) { if (load_gif_frame_descriptors(*m_context).is_error()) {
m_context->error_state = GIFLoadingContext::ErrorState::FailedToLoadFrameDescriptors; m_context->error_state = GIFLoadingContext::ErrorState::FailedToLoadFrameDescriptors;
return 0; return 0;
} }
@ -676,7 +659,7 @@ size_t GIFImageDecoderPlugin::frame_count()
} }
if (m_context->state < GIFLoadingContext::State::FrameDescriptorsLoaded) { if (m_context->state < GIFLoadingContext::State::FrameDescriptorsLoaded) {
if (!load_gif_frame_descriptors(*m_context)) { if (load_gif_frame_descriptors(*m_context).is_error()) {
m_context->error_state = GIFLoadingContext::ErrorState::FailedToLoadFrameDescriptors; m_context->error_state = GIFLoadingContext::ErrorState::FailedToLoadFrameDescriptors;
return 1; return 1;
} }
@ -692,9 +675,9 @@ ErrorOr<ImageFrameDescriptor> GIFImageDecoderPlugin::frame(size_t index)
} }
if (m_context->state < GIFLoadingContext::State::FrameDescriptorsLoaded) { if (m_context->state < GIFLoadingContext::State::FrameDescriptorsLoaded) {
if (!load_gif_frame_descriptors(*m_context)) { if (auto result = load_gif_frame_descriptors(*m_context); result.is_error()) {
m_context->error_state = GIFLoadingContext::ErrorState::FailedToLoadFrameDescriptors; m_context->error_state = GIFLoadingContext::ErrorState::FailedToLoadFrameDescriptors;
return Error::from_string_literal("GIFImageDecoderPlugin: Decoding failed"); return result.release_error();
} }
} }