Coverage Report

Created: 2026-07-16 06:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/symphonia/symphonia-codec-vorbis/src/codebook.rs
Line
Count
Source
1
// Symphonia
2
// Copyright (c) 2019-2026 The Project Symphonia Developers.
3
//
4
// This Source Code Form is subject to the terms of the Mozilla Public
5
// License, v. 2.0. If a copy of the MPL was not distributed with this
6
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
7
8
use symphonia_core::errors::{Result, decode_error};
9
use symphonia_core::io::{
10
    ReadBitsRtl,
11
    vlc::{BitOrder, Codebook, CodebookBuilder, Entry32x32},
12
};
13
14
use super::common::*;
15
16
/// As defined in section 9.2.2 of the Vorbis I specification.
17
///
18
/// `float32_unpack` is intended to translate the packed binary representation of a Vorbis
19
/// codebook float value into the representation used by the decoder for floating point numbers.
20
#[inline(always)]
21
0
fn float32_unpack(x: u32) -> f32 {
22
0
    let mantissa = x & 0x1fffff;
23
0
    let sign = x & 0x80000000;
24
0
    let exponent = (x & 0x7fe00000) >> 21;
25
0
    let value = (mantissa as f32) * 2.0f32.powi(exponent as i32 - 788);
26
0
    if sign == 0 { value } else { -value }
27
0
}
28
29
/// As defined in section 9.2.3 of the Vorbis I specification.
30
///
31
/// The return value for this function is defined to be ’the greatest integer value for which the
32
/// return value to the power of `dimensions` is less than or equal to `entries`.
33
#[inline(always)]
34
0
fn lookup1_values(entries: u32, dimensions: u16) -> u32 {
35
    // Prevent division by 0.
36
0
    if dimensions == 0 {
37
0
        return 0;
38
0
    }
39
40
    // (value ^ dimensions) <= entries
41
    // [(value ^ dimensions) ^ (1 / dimensions)] = lower[entries ^ (1 / dimensions)]
42
    // value = lower[entries ^ (1 / dimensions)]
43
0
    let value = (entries as f32).powf(1.0f32 / f32::from(dimensions)).floor() as u32;
44
45
0
    assert!(value.pow(u32::from(dimensions)) <= entries);
46
47
0
    value
48
0
}
49
50
/// As defined in section 3.2.1 of the Vorbis I specification.
51
0
fn unpack_vq_lookup_type1(
52
0
    multiplicands: &[u16],
53
0
    min_value: f32,
54
0
    delta_value: f32,
55
0
    sequence_p: bool,
56
0
    codebook_entries: u32,
57
0
    codebook_dimensions: u16,
58
0
    lookup_values: u32,
59
0
) -> Vec<f32> {
60
0
    let mut vq_lookup = vec![0.0; codebook_entries as usize * codebook_dimensions as usize];
61
62
0
    for (v, value_vector) in vq_lookup.chunks_exact_mut(codebook_dimensions as usize).enumerate() {
63
0
        let lookup_offset = v as u32;
64
65
0
        let mut last = 0.0;
66
0
        let mut index_divisor = 1;
67
68
0
        for value in value_vector.iter_mut() {
69
0
            let multiplicand_offset = ((lookup_offset / index_divisor) % lookup_values) as usize;
70
71
0
            *value = f32::from(multiplicands[multiplicand_offset]) * delta_value + min_value + last;
72
73
0
            if sequence_p {
74
0
                last = *value;
75
0
            }
76
77
0
            index_divisor *= lookup_values;
78
        }
79
    }
80
81
0
    vq_lookup
82
0
}
83
84
/// As defined in section 3.2.1 of the Vorbis I specification.
85
0
fn unpack_vq_lookup_type2(
86
0
    multiplicands: &[u16],
87
0
    min_value: f32,
88
0
    delta_value: f32,
89
0
    sequence_p: bool,
90
0
    codebook_entries: u32,
91
0
    codebook_dimensions: u16,
92
0
) -> Vec<f32> {
93
0
    let mut vq_lookup = vec![0.0; codebook_entries as usize * codebook_dimensions as usize];
94
95
0
    for (lookup_offset, value_vector) in
96
0
        vq_lookup.chunks_exact_mut(codebook_dimensions as usize).enumerate()
97
    {
98
0
        let mut last = 0.0;
99
0
        let offset = lookup_offset * codebook_dimensions as usize;
100
101
0
        for (offset, value) in (offset..).zip(value_vector.iter_mut()) {
102
0
            *value = f32::from(multiplicands[offset]) * delta_value + min_value + last;
103
104
0
            if sequence_p {
105
0
                last = *value;
106
0
            }
107
        }
108
    }
109
110
0
    vq_lookup
111
0
}
112
113
0
fn synthesize_codewords(code_lens: &[u8]) -> Result<Vec<u32>> {
114
    // This codeword generation algorithm works by maintaining a table of the next valid codeword for
115
    // each codeword length.
116
    //
117
    // Consider a huffman tree. Each level of the tree correlates to a specific length of codeword.
118
    // For example, given a leaf node at level 2 of the huffman tree, that codeword would be 2 bits
119
    // long. Therefore, the table being maintained contains the codeword that would identify the next
120
    // available left-most node in the huffman tree at a given level. Therefore, this table can be
121
    // interrogated to get the next codeword in a simple lookup and the tree will fill-out in the
122
    // canonical order.
123
    //
124
    // Note however that, after selecting a codeword, C, of length N, all codewords of length > N
125
    // cannot use C as a prefix anymore. Therefore, all table entries for codeword lengths > N must
126
    // be updated such that these codewords are skipped over. Likewise, the table must be updated for
127
    // lengths < N to account for jumping between nodes.
128
    //
129
    // This algorithm is a modified version of the one found in the Vorbis reference implementation.
130
0
    let mut codewords = Vec::new();
131
132
0
    let mut next_codeword = [0u32; 33];
133
134
0
    for &len in code_lens.iter() {
135
        // This should always be true.
136
0
        debug_assert!(len <= 32);
137
138
        // Zero length codewords are invalid and ignored.
139
0
        if len == 0 {
140
0
            continue;
141
0
        }
142
143
        // The codeword length, N.
144
0
        let codeword_len = usize::from(len);
145
146
        // The selected codeword, C.
147
0
        let codeword = next_codeword[codeword_len];
148
149
0
        if len < 32 && (codeword >> len) > 0 {
150
0
            return decode_error("vorbis: codebook overspecified");
151
0
        }
152
153
0
        for i in (1..codeword_len + 1).rev() {
154
            // If the least significant bit (LSb) of the next codeword for codewords of length N
155
            // toggles from 1 to 0, that indicates the next-least-LSb will toggle. This means that
156
            // the next codeword will branch off a new parent node. Therefore, the next codeword for
157
            // codewords of length N will use the next codeword for codewords of length N-1 as its
158
            // prefix.
159
0
            if next_codeword[i] & 1 == 1 {
160
0
                if i == 1 {
161
0
                    next_codeword[1] += 1;
162
0
                }
163
0
                else {
164
0
                    next_codeword[i] = next_codeword[i - 1] << 1;
165
0
                }
166
0
                break;
167
0
            }
168
169
            // Otherwise, simply increment the next codeword for codewords of length N by 1. Iterate
170
            // again since there is now 1 branch dangling off the parent node. The parent must now be
171
            // incremented updated in the same way.
172
0
            next_codeword[i] += 1;
173
        }
174
175
        // Given a codeword, C, of length N bits, the codeword is a leaf on the tree and cannot have
176
        // any branches. Otherwise, another codeword would have C as its prefix and that is not
177
        // allowed. Therefore, if the next codeword for codewords of length N+1 uses codeword C as a
178
        // prefix, then the next codeword for codewords of length N+1 must be modified to branch off
179
        // the next codeword of length N instead. Then this modification must be propagated down the
180
        // tree in a similar pattern. In this way, all next codewords for codewords of lengths > N
181
        // that would've used C as a prefix are skipped over and can't be selected regardless of the
182
        // length of the next codeword.
183
0
        let branch = next_codeword[codeword_len];
184
185
0
        for (i, next) in next_codeword[codeword_len..].iter_mut().enumerate().skip(1) {
186
            // If the next codeword for this length of codewords is using the selected codeword, C,
187
            // as a prefix, move it to the next branch.
188
0
            if *next == codeword << i {
189
0
                *next = branch << i;
190
0
            }
191
            else {
192
0
                break;
193
            }
194
        }
195
196
        // Push the codeword.
197
0
        codewords.push(codeword);
198
    }
199
200
    // Check that the tree is fully specified and complete. This means that the next codeword for
201
    // codes of length 1 to 32, inclusive, are saturated.
202
0
    let is_underspecified =
203
0
        next_codeword.iter().enumerate().skip(1).any(|(i, &c)| c & (u32::MAX >> (32 - i)) != 0);
204
205
0
    if is_underspecified {
206
0
        return decode_error("vorbis: codebook underspecified");
207
0
    }
208
209
0
    Ok(codewords)
210
0
}
211
212
pub struct VorbisCodebook {
213
    codebook: Codebook<Entry32x32>,
214
    dimensions: u16,
215
    vq_vec: Option<Vec<f32>>,
216
}
217
218
impl VorbisCodebook {
219
0
    pub fn read<B: ReadBitsRtl>(bs: &mut B) -> Result<Self> {
220
        // Verify codebook synchronization word.
221
0
        let sync = bs.read_bits_leq32(24)?;
222
223
0
        if sync != 0x564342 {
224
0
            return decode_error("vorbis: invalid codebook sync");
225
0
        }
226
227
        // Read codebook number of dimensions and entries.
228
0
        let codebook_dimensions = bs.read_bits_leq32(16)? as u16;
229
0
        let codebook_entries = bs.read_bits_leq32(24)?;
230
231
        // The codebook dimensions cannot be 0 for VQ codebooks.
232
0
        if codebook_dimensions == 0 {
233
0
            return decode_error("vorbis: codebook dimenion cannot be 0");
234
0
        }
235
236
        // Limit the size of codebooks to something reasonable. This limits should never be seen in
237
        // any real bitstream. These limits, together, limit the in-memory size of a VQ codebook to
238
        // 16MB. A scalar codebook is limited to 512kB.
239
0
        if codebook_dimensions > 32 {
240
0
            return decode_error("vorbis: codebook dimension is too large (report this)");
241
0
        }
242
0
        if codebook_entries > 128 * 1024 {
243
0
            return decode_error("vorbis: codebook entries too large (report this)");
244
0
        }
245
246
        // Ordered flag.
247
0
        let is_length_ordered = bs.read_bool()?;
248
249
0
        let mut code_lens = Vec::<u8>::with_capacity(codebook_entries as usize);
250
0
        let mut code_values = Vec::<u32>::with_capacity(codebook_entries as usize);
251
252
0
        if !is_length_ordered {
253
            // Codeword list is not length ordered.
254
0
            let is_sparse = bs.read_bool()?;
255
256
0
            if is_sparse {
257
                // Sparsely packed codeword entry list.
258
0
                for entry in 0..codebook_entries {
259
0
                    let is_used = bs.read_bool()?;
260
261
                    // The codeword list is sparse. Only populate entries that are used.
262
0
                    if is_used {
263
0
                        let code_len = bs.read_bits_leq32(5)? as u8 + 1;
264
265
0
                        code_lens.push(code_len);
266
0
                        code_values.push(entry);
267
0
                    }
268
                }
269
            }
270
            else {
271
                // Densely packed codeword entry list.
272
0
                for _ in 0..codebook_entries {
273
0
                    let code_len = bs.read_bits_leq32(5)? as u8 + 1;
274
0
                    code_lens.push(code_len)
275
                }
276
277
                // The codeword list is not sparse. Populate all values.
278
0
                code_values.extend(0..codebook_entries);
279
            }
280
        }
281
        else {
282
            // Codeword list is length ordered.
283
0
            let mut cur_entry = 0;
284
0
            let mut cur_len = bs.read_bits_leq32(5)? + 1;
285
286
            loop {
287
0
                let num_bits = if codebook_entries > cur_entry {
288
0
                    ilog(codebook_entries - cur_entry)
289
                }
290
                else {
291
0
                    0
292
                };
293
294
0
                let num = bs.read_bits_leq32(num_bits)?;
295
296
0
                code_lens.extend(std::iter::repeat_n(cur_len as u8, num as usize));
297
298
0
                cur_len += 1;
299
0
                cur_entry += num;
300
301
0
                if cur_entry > codebook_entries {
302
0
                    return decode_error("vorbis: invalid codebook");
303
0
                }
304
305
0
                if cur_entry == codebook_entries {
306
0
                    break;
307
0
                }
308
            }
309
310
            // The codeword list is not sparse. Populate all values.
311
0
            code_values.extend(0..cur_entry);
312
        }
313
314
        // Single-entry codebooks are technically invalid because the minimum possible codeword
315
        // length is 1 which requires two entries. If only one entry is provided, then the codebook
316
        // will contain an entry for codeword 0b0 but not for codeword 0b1. However, per the
317
        // Vorbis I specification, errata 20150226, this special-case must be supported by decoding
318
        // both codewords to the same value. Detect single-entry codebooks and add a duplicate entry
319
        // such that both codewords will yield the same value and the codebook will be fully
320
        // specified. Do not support single-entry codebooks for codeword lengths > 1.
321
0
        if code_lens.len() == 1 && code_lens[0] == 1 {
322
0
            code_lens.push(code_lens[0]);
323
0
            code_values.push(code_values[0]);
324
0
        }
325
326
        // Read and unpack vector quantization (VQ) lookup table.
327
0
        let lookup_type = bs.read_bits_leq32(4)?;
328
329
0
        let vq_vec = match lookup_type & 0xf {
330
0
            0 => None,
331
            1 | 2 => {
332
0
                let min_value = float32_unpack(bs.read_bits_leq32(32)?);
333
0
                let delta_value = float32_unpack(bs.read_bits_leq32(32)?);
334
0
                let value_bits = bs.read_bits_leq32(4)? + 1;
335
0
                let sequence_p = bs.read_bool()?;
336
337
                // Lookup type is either 1 or 2 as per outer match.
338
0
                let lookup_values = match lookup_type {
339
0
                    1 => lookup1_values(codebook_entries, codebook_dimensions),
340
0
                    2 => codebook_entries * u32::from(codebook_dimensions),
341
0
                    _ => unreachable!(),
342
                };
343
344
0
                let mut multiplicands = Vec::<u16>::new();
345
346
0
                for _ in 0..lookup_values {
347
0
                    multiplicands.push(bs.read_bits_leq32(value_bits)? as u16);
348
                }
349
350
0
                let vq_lookup = match lookup_type {
351
0
                    1 => unpack_vq_lookup_type1(
352
0
                        &multiplicands,
353
0
                        min_value,
354
0
                        delta_value,
355
0
                        sequence_p,
356
0
                        codebook_entries,
357
0
                        codebook_dimensions,
358
0
                        lookup_values,
359
                    ),
360
0
                    2 => unpack_vq_lookup_type2(
361
0
                        &multiplicands,
362
0
                        min_value,
363
0
                        delta_value,
364
0
                        sequence_p,
365
0
                        codebook_entries,
366
0
                        codebook_dimensions,
367
                    ),
368
0
                    _ => unreachable!(),
369
                };
370
371
0
                Some(vq_lookup)
372
            }
373
0
            _ => return decode_error("vorbis: invalid codeword lookup type"),
374
        };
375
376
        // Generate a canonical list of codewords given the set of codeword lengths.
377
0
        let code_words = synthesize_codewords(&code_lens)?;
378
379
        // Finally, generate the codebook with a reverse (LSb) bit order.
380
0
        let mut builder = CodebookBuilder::new(BitOrder::Reverse);
381
382
        // Read in 4-8 bit-wide blocks.
383
0
        builder.bits_per_read(code_lens.iter().max().copied().unwrap_or(0).clamp(4, 8));
384
385
0
        let codebook = builder.make::<Entry32x32>(&code_words, &code_lens, &code_values)?;
386
387
0
        Ok(VorbisCodebook { codebook, dimensions: codebook_dimensions, vq_vec })
388
0
    }
389
390
    #[inline(always)]
391
0
    pub fn read_scalar<B: ReadBitsRtl>(&self, bs: &mut B) -> Result<u32> {
392
        // An entry in a scalar codebook is just the value.
393
0
        Ok(bs.read_codebook(&self.codebook)?.0)
394
0
    }
395
396
    #[inline(always)]
397
0
    pub fn read_vq<B: ReadBitsRtl>(&self, bs: &mut B) -> Result<&[f32]> {
398
        // An entry in a VQ codebook is the index of the VQ vector.
399
0
        let entry = bs.read_codebook(&self.codebook)?.0;
400
401
0
        if let Some(vq) = &self.vq_vec {
402
0
            let dim = self.dimensions as usize;
403
0
            let start = dim * entry as usize;
404
405
0
            Ok(&vq[start..start + dim])
406
        }
407
        else {
408
0
            decode_error("vorbis: not a vq codebook")
409
        }
410
0
    }
411
412
    #[inline(always)]
413
0
    pub fn dimensions(&self) -> u16 {
414
0
        self.dimensions
415
0
    }
416
}
417
418
#[cfg(test)]
419
mod tests {
420
    use super::{ilog, lookup1_values, synthesize_codewords};
421
422
    #[test]
423
    fn verify_ilog() {
424
        assert_eq!(ilog(0), 0);
425
        assert_eq!(ilog(1), 1);
426
        assert_eq!(ilog(2), 2);
427
        assert_eq!(ilog(3), 2);
428
        assert_eq!(ilog(4), 3);
429
        assert_eq!(ilog(7), 3);
430
    }
431
432
    fn naive_lookup1_values(entries: u32, dimensions: u16) -> u32 {
433
        let mut x = 1u32;
434
435
        // If dimensions is 0, then the only acceptable value for entries is 0 because 0 ^ 0 = 1.
436
        if dimensions > 0 {
437
            loop {
438
                // Since entries is 24-bit, an overflow of u32 will by definition exceed the number
439
                // of entries.
440
                let Some(x_pow) = x.checked_pow(u32::from(dimensions))
441
                else {
442
                    break;
443
                };
444
445
                if x_pow > entries {
446
                    break;
447
                }
448
                x += 1;
449
            }
450
        }
451
452
        x - 1
453
    }
454
455
    #[test]
456
    fn verify_lookup1_values() {
457
        assert_eq!(lookup1_values(0, 0), naive_lookup1_values(0, 1));
458
        assert_eq!(lookup1_values(1, 0), naive_lookup1_values(1, 0));
459
        assert_eq!(lookup1_values(0, 1), naive_lookup1_values(0, 1));
460
        assert_eq!(lookup1_values(1, 1), naive_lookup1_values(1, 1));
461
        assert_eq!(lookup1_values(361, 2), naive_lookup1_values(361, 2));
462
        assert_eq!(lookup1_values(361, 2), naive_lookup1_values(361, 2));
463
        assert_eq!(lookup1_values(560, 3), naive_lookup1_values(560, 3));
464
        assert_eq!(lookup1_values(3, 950), naive_lookup1_values(3, 950));
465
        assert_eq!(lookup1_values(0xffff, 0xff), naive_lookup1_values(0xffff, 0xff));
466
        assert_eq!(lookup1_values(0, u16::MAX), naive_lookup1_values(0, u16::MAX));
467
        assert_eq!(lookup1_values(1, u16::MAX), naive_lookup1_values(1, u16::MAX));
468
        assert_eq!(lookup1_values(0xff_ffff, u16::MAX), naive_lookup1_values(0xff_ffff, u16::MAX));
469
    }
470
471
    #[test]
472
    fn verify_synthesize_codewords() {
473
        const CODEWORD_LENGTHS: &[u8] = &[2, 4, 4, 4, 4, 2, 3, 3];
474
        const EXPECTED_CODEWORDS: &[u32] = &[0, 0x4, 0x5, 0x6, 0x7, 0x2, 0x6, 0x7];
475
        let codewords = synthesize_codewords(CODEWORD_LENGTHS).unwrap();
476
        assert_eq!(&codewords, EXPECTED_CODEWORDS);
477
    }
478
479
    #[test]
480
    fn verify_synthesize_codewords_overspecified() {
481
        // These codebooks are overspecified and shouldn't panic.
482
        assert!(synthesize_codewords(&[1, 1, 1]).is_err());
483
        assert!(synthesize_codewords(&[1, 1, 32]).is_err());
484
    }
485
}