Coverage Report

Created: 2026-09-03 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/vlc/modules/demux/mkv/lzokay.cpp
Line
Count
Source
1
/*
2
 * Copyright (c) 2018 Jack Andersen
3
 * SPDX-License-Identifier: MIT
4
 * https://github.com/AxioDL/lzokay
5
 */
6
7
#ifdef HAVE_CONFIG_H
8
# include "config.h"
9
#endif
10
11
#include "lzokay.hpp"
12
#include <cstring>
13
#include <limits>
14
15
/*
16
 * Based on documentation from the Linux sources: Documentation/lzo.txt
17
 * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/lzo.txt
18
 */
19
20
namespace lzokay {
21
22
static inline uint16_t get_le16(const void *p)
23
27.6k
{
24
27.6k
  uint16_t val;
25
26
27.6k
  memcpy (&val, p, sizeof (val));
27
#ifdef WORDS_BIGENDIAN
28
  val = (val << 8) | (val >> 8);
29
#endif
30
27.6k
  return val;
31
27.6k
}
32
33
constexpr std::size_t Max255Count = std::numeric_limits<size_t>::max() / 255 - 2;
34
35
#define NEEDS_IN(count) \
36
296k
  if (inp + (count) > inp_end) { \
37
312
    dst_size = outp - dst; \
38
312
    return EResult::InputOverrun; \
39
312
  }
40
41
#define NEEDS_OUT(count) \
42
101k
  if (outp + (count) > outp_end) { \
43
2.24k
    dst_size = outp - dst; \
44
2.24k
    return EResult::OutputOverrun; \
45
2.24k
  }
46
47
#define CONSUME_ZERO_BYTE_LENGTH \
48
24.8k
  std::size_t offset; \
49
24.8k
  { \
50
24.8k
    const uint8_t *old_inp = inp; \
51
44.0k
    while (inp < inp_end && *inp == 0) ++inp; \
52
24.8k
    if (inp >= inp_end) { \
53
15
      dst_size = outp - dst; \
54
15
      return EResult::InputOverrun; \
55
15
    } \
56
24.8k
    offset = inp - old_inp; \
57
24.8k
    if (offset > Max255Count) { \
58
0
      dst_size = outp - dst; \
59
0
      return EResult::Error; \
60
0
    } \
61
24.8k
  }
62
63
// constexpr uint32_t M1Marker = 0x0;
64
// constexpr uint32_t M2Marker = 0x40;
65
constexpr uint32_t M3Marker = 0x20;
66
constexpr uint32_t M4Marker = 0x10;
67
68
EResult decompress(const uint8_t* src, std::size_t src_size,
69
                   uint8_t* dst, std::size_t init_dst_size,
70
4.08k
                   std::size_t& dst_size) {
71
4.08k
  dst_size = init_dst_size;
72
73
4.08k
  if (src_size < 3) {
74
137
    dst_size = 0;
75
137
    return EResult::InputOverrun;
76
137
  }
77
78
3.94k
  const uint8_t* inp = src;
79
3.94k
  const uint8_t* inp_end = src + src_size;
80
3.94k
  uint8_t* outp = dst;
81
3.94k
  uint8_t* outp_end = dst + dst_size;
82
3.94k
  uint8_t* lbcur;
83
3.94k
  std::size_t lblen;
84
3.94k
  std::size_t state = 0;
85
3.94k
  std::size_t nstate = 0;
86
87
  /* First byte encoding */
88
3.94k
  if (*inp >= 22) {
89
    /* 22..255 : copy literal string
90
     *           length = (byte - 17) = 4..238
91
     *           state = 4 [ don't copy extra literals ]
92
     *           skip byte
93
     */
94
3.78k
    std::size_t len = *inp++ - uint8_t(17);
95
3.78k
    NEEDS_IN(len)
96
3.71k
    NEEDS_OUT(len)
97
148k
    for (std::size_t i = 0; i < len; ++i)
98
144k
      *outp++ = *inp++;
99
3.71k
    state = 4;
100
3.71k
  } else if (*inp >= 18) {
101
    /* 18..21 : copy 0..3 literals
102
     *          state = (byte - 17) = 0..3  [ copy <state> literals ]
103
     *          skip byte
104
     */
105
12
    nstate = *inp++ - uint8_t(17);
106
12
    state = nstate;
107
12
    NEEDS_IN(nstate)
108
12
    NEEDS_OUT(nstate)
109
36
    for (std::size_t i = 0; i < nstate; ++i)
110
24
      *outp++ = *inp++;
111
12
  }
112
  /* 0..17 : follow regular instruction encoding, see below. It is worth
113
   *         noting that codes 16 and 17 will represent a block copy from
114
   *         the dictionary which is empty, and that they will always be
115
   *         invalid at this place.
116
   */
117
118
99.7k
  while (true) {
119
99.7k
    NEEDS_IN(1)
120
99.7k
    uint8_t inst = *inp++;
121
99.7k
    if (inst & 0xC0) {
122
      /* [M2]
123
       * 1 L L D D D S S  (128..255)
124
       *   Copy 5-8 bytes from block within 2kB distance
125
       *   state = S (copy S literals after this block)
126
       *   length = 5 + L
127
       * Always followed by exactly one byte : H H H H H H H H
128
       *   distance = (H << 3) + D + 1
129
       *
130
       * 0 1 L D D D S S  (64..127)
131
       *   Copy 3-4 bytes from block within 2kB distance
132
       *   state = S (copy S literals after this block)
133
       *   length = 3 + L
134
       * Always followed by exactly one byte : H H H H H H H H
135
       *   distance = (H << 3) + D + 1
136
       */
137
34.5k
      NEEDS_IN(1)
138
34.4k
      lbcur = outp - ((*inp++ << 3) + ((inst >> 2) & 0x7) + 1);
139
34.4k
      lblen = std::size_t(inst >> 5) + 1;
140
34.4k
      nstate = inst & uint8_t(0x3);
141
65.2k
    } else if (inst & M3Marker) {
142
      /* [M3]
143
       * 0 0 1 L L L L L  (32..63)
144
       *   Copy of small block within 16kB distance (preferably less than 34B)
145
       *   length = 2 + (L ?: 31 + (zero_bytes * 255) + non_zero_byte)
146
       * Always followed by exactly one LE16 :  D D D D D D D D : D D D D D D S S
147
       *   distance = D + 1
148
       *   state = S (copy S literals after this block)
149
       */
150
26.7k
      lblen = std::size_t(inst & uint8_t(0x1f)) + 2;
151
26.7k
      if (lblen == 2) {
152
12.1k
        CONSUME_ZERO_BYTE_LENGTH
153
12.1k
        NEEDS_IN(1)
154
12.1k
        lblen += offset * 255 + 31 + *inp++;
155
12.1k
      }
156
26.6k
      NEEDS_IN(2)
157
26.6k
      nstate = get_le16(inp);
158
26.6k
      inp += 2;
159
26.6k
      lbcur = outp - ((nstate >> 2) + 1);
160
26.6k
      nstate &= 0x3;
161
38.5k
    } else if (inst & M4Marker) {
162
      /* [M4]
163
       * 0 0 0 1 H L L L  (16..31)
164
       *   Copy of a block within 16..48kB distance (preferably less than 10B)
165
       *   length = 2 + (L ?: 7 + (zero_bytes * 255) + non_zero_byte)
166
       * Always followed by exactly one LE16 :  D D D D D D D D : D D D D D D S S
167
       *   distance = 16384 + (H << 14) + D
168
       *   state = S (copy S literals after this block)
169
       *   End of stream is reached if distance == 16384
170
       */
171
1.04k
      lblen = std::size_t(inst & uint8_t(0x7)) + 2;
172
1.04k
      if (lblen == 2) {
173
14
        CONSUME_ZERO_BYTE_LENGTH
174
11
        NEEDS_IN(1)
175
11
        lblen += offset * 255 + 7 + *inp++;
176
11
      }
177
1.03k
      NEEDS_IN(2)
178
1.03k
      nstate = get_le16(inp);
179
1.03k
      inp += 2;
180
1.03k
      lbcur = outp - (((inst & 0x8) << 11) + (nstate >> 2));
181
1.03k
      nstate &= 0x3;
182
1.03k
      if (lbcur == outp)
183
1.00k
        break; /* Stream finished */
184
32
      lbcur -= 16384;
185
37.4k
    } else {
186
      /* [M1] Depends on the number of literals copied by the last instruction. */
187
37.4k
      if (state == 0) {
188
        /* If last instruction did not copy any literal (state == 0), this
189
         * encoding will be a copy of 4 or more literal, and must be interpreted
190
         * like this :
191
         *
192
         *    0 0 0 0 L L L L  (0..15)  : copy long literal string
193
         *    length = 3 + (L ?: 15 + (zero_bytes * 255) + non_zero_byte)
194
         *    state = 4  (no extra literals are copied)
195
         */
196
29.4k
        std::size_t len = inst + 3;
197
29.4k
        if (len == 3) {
198
12.6k
          CONSUME_ZERO_BYTE_LENGTH
199
12.6k
          NEEDS_IN(1)
200
12.6k
          len += offset * 255 + 15 + *inp++;
201
12.6k
        }
202
        /* copy_literal_run */
203
29.4k
        NEEDS_IN(len)
204
29.4k
        NEEDS_OUT(len)
205
1.63M
        for (std::size_t i = 0; i < len; ++i)
206
1.60M
          *outp++ = *inp++;
207
27.2k
        state = 4;
208
27.2k
        continue;
209
29.4k
      } else if (state != 4) {
210
        /* If last instruction used to copy between 1 to 3 literals (encoded in
211
         * the instruction's opcode or distance), the instruction is a copy of a
212
         * 2-byte block from the dictionary within a 1kB distance. It is worth
213
         * noting that this instruction provides little savings since it uses 2
214
         * bytes to encode a copy of 2 other bytes but it encodes the number of
215
         * following literals for free. It must be interpreted like this :
216
         *
217
         *    0 0 0 0 D D S S  (0..15)  : copy 2 bytes from <= 1kB distance
218
         *    length = 2
219
         *    state = S (copy S literals after this block)
220
         *  Always followed by exactly one byte : H H H H H H H H
221
         *    distance = (H << 2) + D + 1
222
         */
223
7.92k
        NEEDS_IN(1)
224
7.91k
        nstate = inst & uint8_t(0x3);
225
7.91k
        lbcur = outp - ((inst >> 2) + (*inp++ << 2) + 1);
226
7.91k
        lblen = 2;
227
7.91k
      } else {
228
        /* If last instruction used to copy 4 or more literals (as detected by
229
         * state == 4), the instruction becomes a copy of a 3-byte block from the
230
         * dictionary from a 2..3kB distance, and must be interpreted like this :
231
         *
232
         *    0 0 0 0 D D S S  (0..15)  : copy 3 bytes from 2..3 kB distance
233
         *    length = 3
234
         *    state = S (copy S literals after this block)
235
         *  Always followed by exactly one byte : H H H H H H H H
236
         *    distance = (H << 2) + D + 2049
237
         */
238
97
        NEEDS_IN(1)
239
55
        nstate = inst & uint8_t(0x3);
240
55
        lbcur = outp - ((inst >> 2) + (*inp++ << 2) + 2049);
241
55
        lblen = 3;
242
55
      }
243
37.4k
    }
244
69.0k
    if (lbcur < dst) {
245
374
      dst_size = outp - dst;
246
374
      return EResult::LookbehindOverrun;
247
374
    }
248
68.6k
    NEEDS_IN(nstate)
249
68.6k
    NEEDS_OUT(lblen + nstate)
250
    /* Copy lookbehind */
251
3.18M
    for (std::size_t i = 0; i < lblen; ++i)
252
3.11M
      *outp++ = *lbcur++;
253
68.6k
    state = nstate;
254
    /* Copy literal */
255
113k
    for (std::size_t i = 0; i < nstate; ++i)
256
45.1k
      *outp++ = *inp++;
257
68.6k
  }
258
259
1.00k
  dst_size = outp - dst;
260
1.00k
  if (lblen != 3) /* Ensure terminating M4 was encountered */
261
21
    return EResult::Error;
262
983
  if (inp == inp_end)
263
966
    return EResult::Success;
264
17
  else if (inp < inp_end)
265
17
    return EResult::InputNotConsumed;
266
0
  else
267
0
    return EResult::InputOverrun;
268
983
}
269
270
}