meshopt_rs/index/
buffer.rs

1//! Index buffer encoding and decoding
2
3use crate::INVALID_INDEX;
4
5use std::io::{Read, Write};
6
7use super::{decode_v_byte, encode_v_byte, read_byte, write_byte, DecodeError, IndexEncodingVersion};
8
9const INDEX_HEADER: u8 = 0xe0;
10
11type VertexFifo = [u32; 16];
12
13const DEFAULT_VERTEX_FIFO: VertexFifo = [INVALID_INDEX; 16];
14
15type EdgeFifo = [[u32; 2]; 16];
16
17const DEFAULT_EDGE_FIFO: EdgeFifo = [[INVALID_INDEX; 2]; 16];
18
19const TRIANGLE_INDEX_ORDER: [[usize; 3]; 3] = [[0, 1, 2], [1, 2, 0], [2, 0, 1]];
20
21const CODE_AUX_ENCODING_TABLE: [u8; 16] = [
22    0x00, 0x76, 0x87, 0x56, 0x67, 0x78, 0xa9, 0x86, 0x65, 0x89, 0x68, 0x98, 0x01, 0x69, 0,
23    0, // last two entries aren't used for encoding
24];
25
26fn rotate_triangle(b: u32, c: u32, next: u32) -> i32 {
27    if b == next {
28        1
29    } else {
30        if c == next {
31            2
32        } else {
33            0
34        }
35    }
36}
37
38fn get_edge_fifo(fifo: &EdgeFifo, a: u32, b: u32, c: u32, offset: usize) -> i32 {
39    for i in 0..16 {
40        let index = (offset.wrapping_sub(i + 1)) & 15;
41
42        let e0 = fifo[index][0];
43        let e1 = fifo[index][1];
44
45        if e0 == a && e1 == b {
46            return ((i << 2) | 0) as i32;
47        }
48
49        if e0 == b && e1 == c {
50            return ((i << 2) | 1) as i32;
51        }
52
53        if e0 == c && e1 == a {
54            return ((i << 2) | 2) as i32;
55        }
56    }
57
58    -1
59}
60
61#[inline(always)]
62fn push_edge_fifo(fifo: &mut EdgeFifo, a: u32, b: u32, offset: &mut usize) {
63    fifo[*offset][0] = a;
64    fifo[*offset][1] = b;
65    *offset = (*offset + 1) & 15;
66}
67
68fn get_vertex_fifo(fifo: &VertexFifo, v: u32, offset: usize) -> i32 {
69    for i in 0..16 {
70        let index = (offset.wrapping_sub(i + 1)) & 15;
71
72        if fifo[index] == v {
73            return i as i32;
74        }
75    }
76
77    -1
78}
79
80#[inline(always)]
81fn push_vertex_fifo(fifo: &mut VertexFifo, v: u32, offset: &mut usize, cond: Option<bool>) {
82    fifo[*offset] = v;
83    *offset = (*offset + cond.unwrap_or(true) as usize) & 15;
84}
85
86fn encode_index<W: Write>(data: &mut W, index: u32, last: u32) {
87    let d = index.wrapping_sub(last);
88    let v = (d << 1) ^ (((d as i32) >> 31) as u32);
89
90    encode_v_byte(data, v);
91}
92
93fn decode_index<R: Read>(data: &mut R, last: u32) -> u32 {
94    let v = decode_v_byte(data);
95    let d = (v >> 1) ^ (-((v & 1) as i32) as u32);
96
97    last.wrapping_add(d)
98}
99
100fn get_code_aux_index(v: u8, table: &[u8]) -> i32 {
101    table[0..16]
102        .iter()
103        .position(|t| *t == v)
104        .map(|t| t as i32)
105        .unwrap_or(-1)
106}
107
108fn write_triangle<T>(destination: &mut [T], a: u32, b: u32, c: u32)
109where
110    T: Copy + From<u32>,
111{
112    destination.copy_from_slice(&[T::from(a), T::from(b), T::from(c)]);
113}
114
115/// Encodes index data into an array of bytes that is generally much smaller (<1.5 bytes/triangle) and compresses better (<1 bytes/triangle) compared to original.
116///
117/// Input index buffer must represent a triangle list.
118///
119/// Returns encoded data size on success, `None` on error; the only error condition is if `buffer` doesn't have enough space.
120///
121/// For maximum efficiency the index buffer being encoded has to be optimized for vertex cache and vertex fetch first.
122///
123/// # Arguments
124///
125/// * `buffer`: must contain enough space for the encoded index buffer (use [encode_index_buffer_bound] to compute worst case size)
126pub fn encode_index_buffer(mut buffer: &mut [u8], indices: &[u32], version: IndexEncodingVersion) -> Option<usize> {
127    assert!(indices.len() % 3 == 0);
128
129    let buffer_len = buffer.len();
130
131    // the minimum valid encoding is header, 1 byte per triangle and a 16-byte codeaux table
132    if buffer_len < 1 + indices.len() / 3 + 16 {
133        return None;
134    }
135
136    let version: u8 = version.into();
137
138    write_byte(&mut buffer, INDEX_HEADER | version);
139
140    let mut edgefifo = DEFAULT_EDGE_FIFO;
141    let mut vertexfifo = DEFAULT_VERTEX_FIFO;
142
143    let mut edgefifooffset = 0;
144    let mut vertexfifooffset = 0;
145
146    let mut next = 0;
147    let mut last = 0;
148
149    let (mut code, mut data) = buffer.split_at_mut(indices.len() / 3);
150
151    let fecmax = if version >= 1 { 13 } else { 15 };
152
153    // use static encoding table; it's possible to pack the result and then build an optimal table and repack
154    // for now we keep it simple and use the table that has been generated based on symbol frequency on a training mesh set
155    let codeaux_table = CODE_AUX_ENCODING_TABLE;
156
157    for i in (0..indices.len()).step_by(3) {
158        // make sure we have enough space to write a triangle
159        // each triangle writes at most 16 bytes: 1b for codeaux and 5b for each free index
160        // after this we can be sure we can write without extra bounds checks
161        if data.len() < 16 {
162            return None;
163        }
164
165        let fer = get_edge_fifo(
166            &edgefifo,
167            indices[i + 0],
168            indices[i + 1],
169            indices[i + 2],
170            edgefifooffset,
171        );
172
173        if fer >= 0 && (fer >> 2) < 15 {
174            let order = TRIANGLE_INDEX_ORDER[(fer & 3) as usize];
175
176            let a = indices[i + order[0]];
177            let b = indices[i + order[1]];
178            let c = indices[i + order[2]];
179
180            // encode edge index and vertex fifo index, next or free index
181            let fe = fer >> 2;
182            let fc = get_vertex_fifo(&vertexfifo, c, vertexfifooffset);
183
184            let mut fec = if fc >= 1 && fc < fecmax {
185                fc
186            } else {
187                if c == next {
188                    next += 1;
189                    0
190                } else {
191                    15
192                }
193            };
194
195            if fec == 15 && version >= 1 {
196                // encode last-1 and last+1 to optimize strip-like sequences
197                if c + 1 == last {
198                    fec = 13;
199                    last = c;
200                }
201                if c == last + 1 {
202                    fec = 14;
203                    last = c;
204                }
205            }
206
207            write_byte(&mut code, ((fe << 4) | fec) as u8);
208
209            // note that we need to update the last index since free indices are delta-encoded
210            if fec == 15 {
211                encode_index(&mut data, c, last);
212                last = c;
213            }
214
215            // we only need to push third vertex since first two are likely already in the vertex fifo
216            if fec == 0 || fec >= fecmax {
217                push_vertex_fifo(&mut vertexfifo, c, &mut vertexfifooffset, None);
218            }
219
220            // we only need to push two new edges to edge fifo since the third one is already there
221            push_edge_fifo(&mut edgefifo, c, b, &mut edgefifooffset);
222            push_edge_fifo(&mut edgefifo, a, c, &mut edgefifooffset);
223        } else {
224            let rotation = rotate_triangle(indices[i + 1], indices[i + 2], next);
225            let order = TRIANGLE_INDEX_ORDER[rotation as usize];
226
227            let a = indices[i + order[0]];
228            let b = indices[i + order[1]];
229            let c = indices[i + order[2]];
230
231            // if a/b/c are 0/1/2, we emit a reset code
232            let mut reset = false;
233
234            if a == 0 && b == 1 && c == 2 && next > 0 && version >= 1 {
235                reset = true;
236                next = 0;
237
238                // reset vertex fifo to make sure we don't accidentally reference vertices from that in the future
239                // this makes sure next continues to get incremented instead of being stuck
240                vertexfifo = DEFAULT_VERTEX_FIFO;
241            }
242
243            let fb = get_vertex_fifo(&vertexfifo, b, vertexfifooffset);
244            let fc = get_vertex_fifo(&vertexfifo, c, vertexfifooffset);
245
246            // after rotation, a is almost always equal to next, so we don't waste bits on FIFO encoding for a
247            let fea = if a == next {
248                next += 1;
249                0
250            } else {
251                15
252            };
253            let feb = if fb >= 0 && fb < 14 {
254                fb + 1
255            } else {
256                if b == next {
257                    next += 1;
258                    0
259                } else {
260                    15
261                }
262            };
263            let fec = if fc >= 0 && fc < 14 {
264                fc + 1
265            } else {
266                if c == next {
267                    next += 1;
268                    0
269                } else {
270                    15
271                }
272            };
273
274            // we encode feb & fec in 4 bits using a table if possible, and as a full byte otherwise
275            let codeaux = ((feb << 4) | fec) as u8;
276            let codeauxindex = get_code_aux_index(codeaux, &codeaux_table);
277
278            // <14 encodes an index into codeaux table, 14 encodes fea=0, 15 encodes fea=15
279            if fea == 0 && codeauxindex >= 0 && codeauxindex < 14 && !reset {
280                write_byte(&mut code, ((15 << 4) | codeauxindex) as u8);
281            } else {
282                write_byte(&mut code, ((15 << 4) | 14 | fea) as u8);
283                write_byte(&mut data, codeaux);
284            }
285
286            // note that we need to update the last index since free indices are delta-encoded
287            if fea == 15 {
288                encode_index(&mut data, a, last);
289                last = a;
290            }
291
292            if feb == 15 {
293                encode_index(&mut data, b, last);
294                last = b;
295            }
296
297            if fec == 15 {
298                encode_index(&mut data, c, last);
299                last = c;
300            }
301
302            // only push vertices that weren't already in fifo
303            if fea == 0 || fea == 15 {
304                push_vertex_fifo(&mut vertexfifo, a, &mut vertexfifooffset, None);
305            }
306
307            if feb == 0 || feb == 15 {
308                push_vertex_fifo(&mut vertexfifo, b, &mut vertexfifooffset, None);
309            }
310
311            if fec == 0 || fec == 15 {
312                push_vertex_fifo(&mut vertexfifo, c, &mut vertexfifooffset, None);
313            }
314
315            // all three edges aren't in the fifo; pushing all of them is important so that we can match them for later triangles
316            push_edge_fifo(&mut edgefifo, b, a, &mut edgefifooffset);
317            push_edge_fifo(&mut edgefifo, c, b, &mut edgefifooffset);
318            push_edge_fifo(&mut edgefifo, a, c, &mut edgefifooffset);
319        }
320    }
321
322    // make sure we have enough space to write codeaux table
323    if data.len() < 16 {
324        return None;
325    }
326
327    // add codeaux encoding table to the end of the stream; this is used for decoding codeaux *and* as padding
328    // we need padding for decoding to be able to assume that each triangle is encoded as <= 16 bytes of extra data
329    // this is enough space for aux byte + 5 bytes per varint index which is the absolute worst case for any input
330    for value in &codeaux_table {
331        // decoder assumes that table entries never refer to separately encoded indices
332        assert!((value & 0xf) != 0xf && (value >> 4) != 0xf);
333
334        write_byte(&mut data, *value);
335    }
336
337    // since we encode restarts as codeaux without a table reference, we need to make sure 00 is encoded as a table reference
338    assert_eq!(codeaux_table[0], 0);
339
340    //assert!(data >= buffer + indices.len() / 3 + 16);
341    //assert!(data <= buffer + buffer.len());
342
343    Some(buffer_len - data.len())
344}
345
346/// Returns worst case size requirement for [encode_index_buffer].
347pub fn encode_index_buffer_bound(index_count: usize, vertex_count: usize) -> usize {
348    assert_eq!(index_count % 3, 0);
349
350    // compute number of bits required for each index
351    let mut vertex_bits = 1;
352
353    while vertex_bits < 32 && vertex_count > (1 << vertex_bits) {
354        vertex_bits += 1;
355    }
356
357    // worst-case encoding is 2 header bytes + 3 varint-7 encoded index deltas
358    let vertex_groups = (vertex_bits + 1 + 6) / 7;
359
360    1 + (index_count / 3) * (2 + 3 * vertex_groups) + 16
361}
362
363/// Decodes index data from an array of bytes generated by [encode_index_buffer].
364///
365/// Returns `Ok` if decoding was successful, and an error otherwise.
366/// The decoder is safe to use for untrusted input, but it may produce garbage data (e.g. out of range indices).
367///
368/// # Arguments
369///
370/// * `destination`: must contain the exact space for the resulting index buffer
371pub fn decode_index_buffer<T>(destination: &mut [T], buffer: &[u8]) -> Result<(), DecodeError>
372where
373    T: Copy + From<u32>,
374{
375    assert_eq!(destination.len() % 3, 0);
376    //assert!(index_size == 2 || index_size == 4);
377
378    // the minimum valid encoding is header, 1 byte per triangle and a 16-byte codeaux table
379    if buffer.len() < 1 + destination.len() / 3 + 16 {
380        return Err(DecodeError::UnexpectedEof);
381    }
382
383    if (buffer[0] & 0xf0) != INDEX_HEADER {
384        return Err(DecodeError::InvalidHeader);
385    }
386
387    let version = buffer[0] & 0x0f;
388    if version > 1 {
389        return Err(DecodeError::UnsupportedVersion);
390    }
391
392    let mut edgefifo = DEFAULT_EDGE_FIFO;
393    let mut vertexfifo = DEFAULT_VERTEX_FIFO;
394
395    let mut edgefifooffset: usize = 0;
396    let mut vertexfifooffset: usize = 0;
397
398    let mut next: u32 = 0;
399    let mut last: u32 = 0;
400
401    let fecmax = if version >= 1 { 13 } else { 15 };
402
403    // since we store 16-byte codeaux table at the end, triangle data has to begin before data_safe_end
404    let (mut code, mut data, codeaux_table, data_safe_end) = {
405        let (code, data) = buffer[1..].split_at(destination.len() / 3);
406        let data_safe_end = data.len() - 16;
407        let codeaux_table = &data[data_safe_end..];
408        (code, std::io::Cursor::new(data), codeaux_table, data_safe_end)
409    };
410
411    for dst in destination.chunks_exact_mut(3) {
412        // make sure we have enough data to read for a triangle
413        // each triangle reads at most 16 bytes of data: 1b for codeaux and 5b for each free index
414        // after this we can be sure we can read without extra bounds checks
415        if data.position() > data_safe_end as u64 {
416            return Err(DecodeError::UnexpectedEof);
417        }
418
419        let codetri = read_byte(&mut code) as usize;
420
421        if codetri < 0xf0 {
422            let fe = codetri >> 4;
423
424            // fifo reads are wrapped around 16 entry buffer
425            let a = edgefifo[(edgefifooffset.wrapping_sub(fe + 1)) & 15][0];
426            let b = edgefifo[(edgefifooffset.wrapping_sub(fe + 1)) & 15][1];
427
428            let fec = codetri & 15;
429
430            // note: this is the most common path in the entire decoder
431            // inside this if we try to stay branchless (by using cmov/etc.) since these aren't predictable
432            if fec < fecmax {
433                // fifo reads are wrapped around 16 entry buffer
434                let cf = vertexfifo[(vertexfifooffset.wrapping_sub(fec + 1)) & 15];
435                let c = if fec == 0 { next } else { cf };
436
437                let fec0 = fec == 0;
438                next += fec0 as u32;
439
440                // output triangle
441                write_triangle(dst, a, b, c);
442
443                // push vertex/edge fifo must match the encoding step *exactly* otherwise the data will not be decoded correctly
444                push_vertex_fifo(&mut vertexfifo, c, &mut vertexfifooffset, Some(fec0));
445
446                push_edge_fifo(&mut edgefifo, c, b, &mut edgefifooffset);
447                push_edge_fifo(&mut edgefifo, a, c, &mut edgefifooffset);
448            } else {
449                // fec - (fec ^ 3) decodes 13, 14 into -1, 1
450                // note that we need to update the last index since free indices are delta-encoded
451                let c = if fec != 15 {
452                    last.wrapping_add((fec.wrapping_sub(fec ^ 3)) as u32)
453                } else {
454                    decode_index(&mut data, last)
455                };
456                last = c;
457
458                // output triangle
459                write_triangle(dst, a, b, c);
460
461                // push vertex/edge fifo must match the encoding step *exactly* otherwise the data will not be decoded correctly
462                push_vertex_fifo(&mut vertexfifo, c, &mut vertexfifooffset, None);
463
464                push_edge_fifo(&mut edgefifo, c, b, &mut edgefifooffset);
465                push_edge_fifo(&mut edgefifo, a, c, &mut edgefifooffset);
466            }
467        } else {
468            // fast path: read codeaux from the table
469            if codetri < 0xfe {
470                let codeaux = codeaux_table[codetri & 15];
471
472                // note: table can't contain feb/fec=15
473                let feb = codeaux >> 4;
474                let fec = codeaux & 15;
475
476                // fifo reads are wrapped around 16 entry buffer
477                // also note that we increment next for all three vertices before decoding indices - this matches encoder behavior
478                let a = next;
479                next += 1;
480
481                let bf = vertexfifo[(vertexfifooffset.wrapping_sub(feb as usize)) & 15];
482                let b = if feb == 0 { next } else { bf };
483
484                let feb0 = feb == 0;
485                next += feb0 as u32;
486
487                let cf = vertexfifo[(vertexfifooffset.wrapping_sub(fec as usize)) & 15];
488                let c = if fec == 0 { next } else { cf };
489
490                let fec0 = fec == 0;
491                next += fec0 as u32;
492
493                // output triangle
494                write_triangle(dst, a, b, c);
495
496                // push vertex/edge fifo must match the encoding step *exactly* otherwise the data will not be decoded correctly
497                push_vertex_fifo(&mut vertexfifo, a, &mut vertexfifooffset, None);
498                push_vertex_fifo(&mut vertexfifo, b, &mut vertexfifooffset, Some(feb0));
499                push_vertex_fifo(&mut vertexfifo, c, &mut vertexfifooffset, Some(fec0));
500
501                push_edge_fifo(&mut edgefifo, b, a, &mut edgefifooffset);
502                push_edge_fifo(&mut edgefifo, c, b, &mut edgefifooffset);
503                push_edge_fifo(&mut edgefifo, a, c, &mut edgefifooffset);
504            } else {
505                // slow path: read a full byte for codeaux instead of using a table lookup
506                let codeaux = read_byte(&mut data);
507
508                let fea = if codetri == 0xfe { 0 } else { 15 };
509                let feb = codeaux >> 4;
510                let fec = codeaux & 15;
511
512                // reset: codeaux is 0 but encoded as not-a-table
513                if codeaux == 0 {
514                    next = 0;
515                }
516
517                // fifo reads are wrapped around 16 entry buffer
518                // also note that we increment next for all three vertices before decoding indices - this matches encoder behavior
519                let mut a = if fea == 0 {
520                    let n = next;
521                    next += 1;
522                    n
523                } else {
524                    0
525                };
526                let mut b = if feb == 0 {
527                    let n = next;
528                    next += 1;
529                    n
530                } else {
531                    vertexfifo[(vertexfifooffset.wrapping_sub(feb as usize)) & 15]
532                };
533                let mut c = if fec == 0 {
534                    let n = next;
535                    next += 1;
536                    n
537                } else {
538                    vertexfifo[(vertexfifooffset.wrapping_sub(fec as usize)) & 15]
539                };
540
541                // note that we need to update the last index since free indices are delta-encoded
542                if fea == 15 {
543                    a = decode_index(&mut data, last);
544                    last = a;
545                }
546
547                if feb == 15 {
548                    b = decode_index(&mut data, last);
549                    last = b;
550                }
551
552                if fec == 15 {
553                    c = decode_index(&mut data, last);
554                    last = c;
555                }
556
557                // output triangle
558                write_triangle(dst, a, b, c);
559
560                // push vertex/edge fifo must match the encoding step *exactly* otherwise the data will not be decoded correctly
561                push_vertex_fifo(&mut vertexfifo, a, &mut vertexfifooffset, None);
562                push_vertex_fifo(
563                    &mut vertexfifo,
564                    b,
565                    &mut vertexfifooffset,
566                    Some((feb == 0) | (feb == 15)),
567                );
568                push_vertex_fifo(
569                    &mut vertexfifo,
570                    c,
571                    &mut vertexfifooffset,
572                    Some((fec == 0) | (fec == 15)),
573                );
574
575                push_edge_fifo(&mut edgefifo, b, a, &mut edgefifooffset);
576                push_edge_fifo(&mut edgefifo, c, b, &mut edgefifooffset);
577                push_edge_fifo(&mut edgefifo, a, c, &mut edgefifooffset);
578            }
579        }
580    }
581
582    if data.position() == data_safe_end as u64 {
583        Ok(())
584    } else {
585        // we should've read all data bytes and stopped at the boundary between data and codeaux table
586        Err(DecodeError::ExtraBytes)
587    }
588}
589
590#[cfg(test)]
591mod test {
592    use super::*;
593
594    // note: 4 6 5 triangle here is a combo-breaker:
595    // we encode it without rotating, a=next, c=next - this means we do *not* bump next to 6
596    // which means that the next triangle can't be encoded via next sequencing!
597    const INDEX_BUFFER: [u32; 12] = [0, 1, 2, 2, 1, 3, 4, 6, 5, 7, 8, 9];
598
599    const INDEX_DATA_V0: [u8; 27] = [
600        0xe0, 0xf0, 0x10, 0xfe, 0xff, 0xf0, 0x0c, 0xff, 0x02, 0x02, 0x02, 0x00, 0x76, 0x87, 0x56, 0x67, 0x78, 0xa9,
601        0x86, 0x65, 0x89, 0x68, 0x98, 0x01, 0x69, 0x00, 0x00,
602    ];
603
604    // note: this exercises two features of v1 format, restarts (0 1 2) and last
605    const INDEX_BUFFER_TRICKY: [u32; 15] = [0, 1, 2, 2, 1, 3, 0, 1, 2, 2, 1, 5, 2, 1, 4];
606
607    const INDEX_DATA_V1: [u8; 24] = [
608        0xe1, 0xf0, 0x10, 0xfe, 0x1f, 0x3d, 0x00, 0x0a, 0x00, 0x76, 0x87, 0x56, 0x67, 0x78, 0xa9, 0x86, 0x65, 0x89,
609        0x68, 0x98, 0x01, 0x69, 0x00, 0x00,
610    ];
611
612    #[test]
613    fn test_decode_index_v0() {
614        let mut decoded: [u32; INDEX_BUFFER.len()] = Default::default();
615
616        assert!(decode_index_buffer(&mut decoded, &INDEX_DATA_V0).is_ok());
617        assert_eq!(decoded, INDEX_BUFFER);
618    }
619
620    #[test]
621    fn test_decode_index_v1() {
622        let mut decoded: [u32; INDEX_BUFFER_TRICKY.len()] = Default::default();
623
624        assert!(decode_index_buffer(&mut decoded, &INDEX_DATA_V1).is_ok());
625        assert_eq!(decoded, INDEX_BUFFER_TRICKY);
626    }
627
628    #[test]
629    fn test_decode_index_16() {
630        let buffer = encode_test_index();
631
632        #[derive(Clone, Copy, Default)]
633        struct U16(u16);
634
635        impl From<u32> for U16 {
636            fn from(index: u32) -> Self {
637                Self { 0: index as u16 }
638            }
639        }
640
641        let mut decoded = [U16::default(); INDEX_BUFFER.len()];
642        assert!(decode_index_buffer(&mut decoded, &buffer).is_ok());
643
644        assert!(decoded.iter().enumerate().all(|(i, v)| v.0 as u32 == INDEX_BUFFER[i]));
645    }
646
647    #[test]
648    fn test_encode_index_memory_safe() {
649        let mut buffer = encode_test_index();
650
651        // check that encode is memory-safe
652        for i in 0..=buffer.len() {
653            let result = encode_index_buffer(&mut buffer[0..i], &INDEX_BUFFER, IndexEncodingVersion::default());
654
655            if i == buffer.len() {
656                assert_eq!(result, Some(buffer.len()));
657            } else {
658                assert_eq!(result, None);
659            }
660        }
661    }
662
663    fn encode_test_index() -> Vec<u8> {
664        let mut buffer = vec![0; encode_index_buffer_bound(INDEX_BUFFER.len(), 10)];
665
666        let written = encode_index_buffer(&mut buffer, &INDEX_BUFFER, IndexEncodingVersion::default()).unwrap();
667        buffer.resize_with(written, Default::default);
668
669        buffer
670    }
671
672    #[test]
673    fn test_decode_index_memory_safe() {
674        let buffer = encode_test_index();
675
676        // check that decode is memory-safe
677        let mut decoded: [u32; INDEX_BUFFER.len()] = Default::default();
678
679        for i in 0..=buffer.len() {
680            let result = decode_index_buffer(&mut decoded, &buffer[0..i]);
681
682            if i == buffer.len() {
683                assert!(result.is_ok());
684            } else {
685                assert!(result.is_err());
686            }
687        }
688    }
689
690    #[test]
691    fn test_decode_index_reject_extra_bytes() {
692        let mut buffer = encode_test_index();
693
694        // check that decoder doesn't accept extra bytes after a valid stream
695        buffer.push(0);
696
697        let mut decoded: [u32; INDEX_BUFFER.len()] = Default::default();
698        assert!(decode_index_buffer(&mut decoded, &buffer).is_err());
699    }
700
701    #[test]
702    fn test_decode_index_reject_malformed_headers() {
703        let mut buffer = encode_test_index();
704
705        // check that decoder doesn't accept malformed headers
706        buffer[0] = 0;
707
708        let mut decoded: [u32; INDEX_BUFFER.len()] = Default::default();
709        assert!(decode_index_buffer(&mut decoded, &buffer).is_err());
710    }
711
712    #[test]
713    fn test_decode_index_reject_invalid_version() {
714        let mut buffer = encode_test_index();
715
716        // check that decoder doesn't accept invalid version
717        buffer[0] |= 0x0f;
718
719        let mut decoded: [u32; INDEX_BUFFER.len()] = Default::default();
720        assert!(decode_index_buffer(&mut decoded, &buffer).is_err());
721    }
722
723    #[test]
724    fn test_roundtrip_index_tricky() {
725        let mut buffer = Vec::new();
726        buffer.resize_with(
727            encode_index_buffer_bound(INDEX_BUFFER_TRICKY.len(), 6),
728            Default::default,
729        );
730
731        let written = encode_index_buffer(&mut buffer, &INDEX_BUFFER_TRICKY, IndexEncodingVersion::default()).unwrap();
732        buffer.resize_with(written, Default::default);
733
734        let mut decoded: [u32; INDEX_BUFFER_TRICKY.len()] = Default::default();
735        assert!(decode_index_buffer(&mut decoded, &buffer).is_ok());
736        assert_eq!(decoded, INDEX_BUFFER_TRICKY);
737    }
738
739    #[test]
740    fn test_encode_index_empty() {
741        let mut buffer = Vec::new();
742        buffer.resize_with(encode_index_buffer_bound(0, 0), Default::default);
743
744        let written = encode_index_buffer(&mut buffer, &[], IndexEncodingVersion::default()).unwrap();
745        buffer.resize_with(written, Default::default);
746
747        assert!(decode_index_buffer::<u32>(&mut [], &buffer).is_ok());
748    }
749}