Coverage Report

Created: 2025-11-02 07:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibGfx/ImageFormats/BooleanDecoder.cpp
Line
Count
Source
1
/*
2
 * Copyright (c) 2021, Hunter Salyer <thefalsehonesty@gmail.com>
3
 * Copyright (c) 2022, Gregory Bertilson <zaggy1024@gmail.com>
4
 *
5
 * SPDX-License-Identifier: BSD-2-Clause
6
 */
7
8
#include <AK/BuiltinWrappers.h>
9
#include <AK/Debug.h>
10
#include <AK/Endian.h>
11
12
#include "BooleanDecoder.h"
13
14
namespace Gfx {
15
16
// 9.2.1 Initialization process for Boolean decoder
17
ErrorOr<BooleanDecoder> BooleanDecoder::initialize(ReadonlyBytes data)
18
5.81k
{
19
5.81k
    if (data.size() == 0)
20
13
        return Error::from_string_literal("Size of decoder range cannot be zero");
21
22
    // NOTE: This implementation is shared between VP8 and VP9. Therefore, we do not check the
23
    //       marker bit at the start of the range decode that is required in the VP9 specification.
24
    //       This is instead handled by the function that instantiates all range decoders for the
25
    //       VP9 decoder.
26
27
    // NOTE: As noted below in fill_reservoir(), we read in multi-byte-sized chunks,
28
    //       so here we will deviate from the standard to count in bytes rather than bits.
29
5.79k
    return BooleanDecoder { data.data(), data.size() };
30
5.81k
}
31
32
// Instead of filling the value field one bit at a time as the spec suggests, we store the
33
// data to be read in a reservoir of greater than one byte. This allows us to read out data
34
// for the entire reservoir at once, avoiding a lot of branch misses in read_bool().
35
void BooleanDecoder::fill_reservoir()
36
409M
{
37
409M
    if (m_value_bits_left > 8)
38
409M
        return;
39
40
    // Defer errors until the decode is finalized, so the work to check for errors and return them only has
41
    // to be done once. Not refilling the reservoir here will only result in reading out all zeroes until
42
    // the range decode is finished.
43
772k
    if (m_bytes_left == 0) {
44
12.2k
        dbgln_if(VPX_DEBUG, "BooleanDecoder has read past the end of the coded range");
45
12.2k
        m_overread = true;
46
12.2k
        return;
47
12.2k
    }
48
49
    // Read the data into the most significant bits of a variable.
50
760k
    auto read_size = min<size_t>(reserve_bytes, m_bytes_left);
51
760k
    ValueType read_value = 0;
52
760k
    memcpy(&read_value, m_data, read_size);
53
760k
    read_value = AK::convert_between_host_and_big_endian(read_value);
54
55
    // Skip the number of bytes read in the data.
56
760k
    m_data += read_size;
57
760k
    m_bytes_left -= read_size;
58
59
    // Shift the value that was read to be less significant than the least significant bit available in the reservoir.
60
760k
    read_value >>= m_value_bits_left;
61
760k
    m_value |= read_value;
62
760k
    m_value_bits_left += read_size * 8;
63
760k
}
64
65
// 9.2.2 Boolean decoding process
66
bool BooleanDecoder::read_bool(u8 probability)
67
409M
{
68
409M
    auto split = 1u + (((m_range - 1u) * probability) >> 8u);
69
    // The actual value being read resides in the most significant 8 bits
70
    // of the value field, so we shift the split into that range for comparison.
71
409M
    auto split_shifted = static_cast<ValueType>(split) << reserve_bits;
72
409M
    bool return_bool;
73
74
409M
    if (m_value < split_shifted) {
75
308M
        m_range = split;
76
308M
        return_bool = false;
77
308M
    } else {
78
101M
        m_range -= split;
79
101M
        m_value -= split_shifted;
80
101M
        return_bool = true;
81
101M
    }
82
83
409M
    u8 bits_to_shift_into_range = count_leading_zeroes(m_range) - ((sizeof(m_range) - 1) * 8);
84
409M
    m_range <<= bits_to_shift_into_range;
85
409M
    m_value <<= bits_to_shift_into_range;
86
409M
    m_value_bits_left -= bits_to_shift_into_range;
87
88
409M
    fill_reservoir();
89
90
409M
    return return_bool;
91
409M
}
92
93
// 9.2.4 Parsing process for read_literal
94
u8 BooleanDecoder::read_literal(u8 bits)
95
6.39M
{
96
6.39M
    u8 return_value = 0;
97
13.0M
    for (size_t i = 0; i < bits; i++) {
98
6.60M
        return_value = (2 * return_value) + read_bool(128);
99
6.60M
    }
100
6.39M
    return return_value;
101
6.39M
}
102
103
ErrorOr<void> BooleanDecoder::finish_decode()
104
5.66k
{
105
5.66k
    if (m_overread)
106
738
        return Error::from_string_literal("Range decoder was read past the end of its data");
107
108
#if VPX_DEBUG
109
    // 9.2.3 Exit process for Boolean decoder
110
    //
111
    // This process is invoked when the function exit_bool( ) is called from the syntax structure.
112
    //
113
    // The padding syntax element is read using the f(BoolMaxBits) parsing process.
114
    //
115
    // It is a requirement of bitstream conformance that padding is equal to 0.
116
    //
117
    // NOTE: This requirement holds up for all of our WebP lossy test inputs, as well.
118
    bool padding_good = true;
119
120
    if (m_value != 0)
121
        padding_good = false;
122
123
    while (m_bytes_left > 0) {
124
        if (*m_data != 0)
125
            padding_good = false;
126
        m_data++;
127
        m_bytes_left--;
128
    }
129
130
    if (!padding_good)
131
        return Error::from_string_literal("Range decoder padding was non-zero");
132
#endif
133
134
4.92k
    return {};
135
5.66k
}
136
137
}