use crate::INVALID_INDEX;
use std::io::{Read, Write};
use super::{decode_v_byte, encode_v_byte, read_byte, write_byte, DecodeError, IndexEncodingVersion};
const INDEX_HEADER: u8 = 0xe0;
type VertexFifo = [u32; 16];
const DEFAULT_VERTEX_FIFO: VertexFifo = [INVALID_INDEX; 16];
type EdgeFifo = [[u32; 2]; 16];
const DEFAULT_EDGE_FIFO: EdgeFifo = [[INVALID_INDEX; 2]; 16];
const TRIANGLE_INDEX_ORDER: [[usize; 3]; 3] = [[0, 1, 2], [1, 2, 0], [2, 0, 1]];
const CODE_AUX_ENCODING_TABLE: [u8; 16] = [
0x00, 0x76, 0x87, 0x56, 0x67, 0x78, 0xa9, 0x86, 0x65, 0x89, 0x68, 0x98, 0x01, 0x69, 0,
0, ];
fn rotate_triangle(b: u32, c: u32, next: u32) -> i32 {
if b == next {
1
} else {
if c == next {
2
} else {
0
}
}
}
fn get_edge_fifo(fifo: &EdgeFifo, a: u32, b: u32, c: u32, offset: usize) -> i32 {
for i in 0..16 {
let index = (offset.wrapping_sub(i + 1)) & 15;
let e0 = fifo[index][0];
let e1 = fifo[index][1];
if e0 == a && e1 == b {
return ((i << 2) | 0) as i32;
}
if e0 == b && e1 == c {
return ((i << 2) | 1) as i32;
}
if e0 == c && e1 == a {
return ((i << 2) | 2) as i32;
}
}
-1
}
#[inline(always)]
fn push_edge_fifo(fifo: &mut EdgeFifo, a: u32, b: u32, offset: &mut usize) {
fifo[*offset][0] = a;
fifo[*offset][1] = b;
*offset = (*offset + 1) & 15;
}
fn get_vertex_fifo(fifo: &VertexFifo, v: u32, offset: usize) -> i32 {
for i in 0..16 {
let index = (offset.wrapping_sub(i + 1)) & 15;
if fifo[index] == v {
return i as i32;
}
}
-1
}
#[inline(always)]
fn push_vertex_fifo(fifo: &mut VertexFifo, v: u32, offset: &mut usize, cond: Option<bool>) {
fifo[*offset] = v;
*offset = (*offset + cond.unwrap_or(true) as usize) & 15;
}
fn encode_index<W: Write>(data: &mut W, index: u32, last: u32) {
let d = index.wrapping_sub(last);
let v = (d << 1) ^ (((d as i32) >> 31) as u32);
encode_v_byte(data, v);
}
fn decode_index<R: Read>(data: &mut R, last: u32) -> u32 {
let v = decode_v_byte(data);
let d = (v >> 1) ^ (-((v & 1) as i32) as u32);
last.wrapping_add(d)
}
fn get_code_aux_index(v: u8, table: &[u8]) -> i32 {
table[0..16]
.iter()
.position(|t| *t == v)
.map(|t| t as i32)
.unwrap_or(-1)
}
fn write_triangle<T>(destination: &mut [T], a: u32, b: u32, c: u32)
where
T: Copy + From<u32>,
{
destination.copy_from_slice(&[T::from(a), T::from(b), T::from(c)]);
}
pub fn encode_index_buffer(mut buffer: &mut [u8], indices: &[u32], version: IndexEncodingVersion) -> Option<usize> {
assert!(indices.len() % 3 == 0);
let buffer_len = buffer.len();
if buffer_len < 1 + indices.len() / 3 + 16 {
return None;
}
let version: u8 = version.into();
write_byte(&mut buffer, INDEX_HEADER | version);
let mut edgefifo = DEFAULT_EDGE_FIFO;
let mut vertexfifo = DEFAULT_VERTEX_FIFO;
let mut edgefifooffset = 0;
let mut vertexfifooffset = 0;
let mut next = 0;
let mut last = 0;
let (mut code, mut data) = buffer.split_at_mut(indices.len() / 3);
let fecmax = if version >= 1 { 13 } else { 15 };
let codeaux_table = CODE_AUX_ENCODING_TABLE;
for i in (0..indices.len()).step_by(3) {
if data.len() < 16 {
return None;
}
let fer = get_edge_fifo(
&edgefifo,
indices[i + 0],
indices[i + 1],
indices[i + 2],
edgefifooffset,
);
if fer >= 0 && (fer >> 2) < 15 {
let order = TRIANGLE_INDEX_ORDER[(fer & 3) as usize];
let a = indices[i + order[0]];
let b = indices[i + order[1]];
let c = indices[i + order[2]];
let fe = fer >> 2;
let fc = get_vertex_fifo(&vertexfifo, c, vertexfifooffset);
let mut fec = if fc >= 1 && fc < fecmax {
fc
} else {
if c == next {
next += 1;
0
} else {
15
}
};
if fec == 15 && version >= 1 {
if c + 1 == last {
fec = 13;
last = c;
}
if c == last + 1 {
fec = 14;
last = c;
}
}
write_byte(&mut code, ((fe << 4) | fec) as u8);
if fec == 15 {
encode_index(&mut data, c, last);
last = c;
}
if fec == 0 || fec >= fecmax {
push_vertex_fifo(&mut vertexfifo, c, &mut vertexfifooffset, None);
}
push_edge_fifo(&mut edgefifo, c, b, &mut edgefifooffset);
push_edge_fifo(&mut edgefifo, a, c, &mut edgefifooffset);
} else {
let rotation = rotate_triangle(indices[i + 1], indices[i + 2], next);
let order = TRIANGLE_INDEX_ORDER[rotation as usize];
let a = indices[i + order[0]];
let b = indices[i + order[1]];
let c = indices[i + order[2]];
let mut reset = false;
if a == 0 && b == 1 && c == 2 && next > 0 && version >= 1 {
reset = true;
next = 0;
vertexfifo = DEFAULT_VERTEX_FIFO;
}
let fb = get_vertex_fifo(&vertexfifo, b, vertexfifooffset);
let fc = get_vertex_fifo(&vertexfifo, c, vertexfifooffset);
let fea = if a == next {
next += 1;
0
} else {
15
};
let feb = if fb >= 0 && fb < 14 {
fb + 1
} else {
if b == next {
next += 1;
0
} else {
15
}
};
let fec = if fc >= 0 && fc < 14 {
fc + 1
} else {
if c == next {
next += 1;
0
} else {
15
}
};
let codeaux = ((feb << 4) | fec) as u8;
let codeauxindex = get_code_aux_index(codeaux, &codeaux_table);
if fea == 0 && codeauxindex >= 0 && codeauxindex < 14 && !reset {
write_byte(&mut code, ((15 << 4) | codeauxindex) as u8);
} else {
write_byte(&mut code, ((15 << 4) | 14 | fea) as u8);
write_byte(&mut data, codeaux);
}
if fea == 15 {
encode_index(&mut data, a, last);
last = a;
}
if feb == 15 {
encode_index(&mut data, b, last);
last = b;
}
if fec == 15 {
encode_index(&mut data, c, last);
last = c;
}
if fea == 0 || fea == 15 {
push_vertex_fifo(&mut vertexfifo, a, &mut vertexfifooffset, None);
}
if feb == 0 || feb == 15 {
push_vertex_fifo(&mut vertexfifo, b, &mut vertexfifooffset, None);
}
if fec == 0 || fec == 15 {
push_vertex_fifo(&mut vertexfifo, c, &mut vertexfifooffset, None);
}
push_edge_fifo(&mut edgefifo, b, a, &mut edgefifooffset);
push_edge_fifo(&mut edgefifo, c, b, &mut edgefifooffset);
push_edge_fifo(&mut edgefifo, a, c, &mut edgefifooffset);
}
}
if data.len() < 16 {
return None;
}
for value in &codeaux_table {
assert!((value & 0xf) != 0xf && (value >> 4) != 0xf);
write_byte(&mut data, *value);
}
assert_eq!(codeaux_table[0], 0);
Some(buffer_len - data.len())
}
pub fn encode_index_buffer_bound(index_count: usize, vertex_count: usize) -> usize {
assert_eq!(index_count % 3, 0);
let mut vertex_bits = 1;
while vertex_bits < 32 && vertex_count > (1 << vertex_bits) {
vertex_bits += 1;
}
let vertex_groups = (vertex_bits + 1 + 6) / 7;
1 + (index_count / 3) * (2 + 3 * vertex_groups) + 16
}
pub fn decode_index_buffer<T>(destination: &mut [T], buffer: &[u8]) -> Result<(), DecodeError>
where
T: Copy + From<u32>,
{
assert_eq!(destination.len() % 3, 0);
if buffer.len() < 1 + destination.len() / 3 + 16 {
return Err(DecodeError::UnexpectedEof);
}
if (buffer[0] & 0xf0) != INDEX_HEADER {
return Err(DecodeError::InvalidHeader);
}
let version = buffer[0] & 0x0f;
if version > 1 {
return Err(DecodeError::UnsupportedVersion);
}
let mut edgefifo = DEFAULT_EDGE_FIFO;
let mut vertexfifo = DEFAULT_VERTEX_FIFO;
let mut edgefifooffset: usize = 0;
let mut vertexfifooffset: usize = 0;
let mut next: u32 = 0;
let mut last: u32 = 0;
let fecmax = if version >= 1 { 13 } else { 15 };
let (mut code, mut data, codeaux_table, data_safe_end) = {
let (code, data) = buffer[1..].split_at(destination.len() / 3);
let data_safe_end = data.len() - 16;
let codeaux_table = &data[data_safe_end..];
(code, std::io::Cursor::new(data), codeaux_table, data_safe_end)
};
for dst in destination.chunks_exact_mut(3) {
if data.position() > data_safe_end as u64 {
return Err(DecodeError::UnexpectedEof);
}
let codetri = read_byte(&mut code) as usize;
if codetri < 0xf0 {
let fe = codetri >> 4;
let a = edgefifo[(edgefifooffset.wrapping_sub(fe + 1)) & 15][0];
let b = edgefifo[(edgefifooffset.wrapping_sub(fe + 1)) & 15][1];
let fec = codetri & 15;
if fec < fecmax {
let cf = vertexfifo[(vertexfifooffset.wrapping_sub(fec + 1)) & 15];
let c = if fec == 0 { next } else { cf };
let fec0 = fec == 0;
next += fec0 as u32;
write_triangle(dst, a, b, c);
push_vertex_fifo(&mut vertexfifo, c, &mut vertexfifooffset, Some(fec0));
push_edge_fifo(&mut edgefifo, c, b, &mut edgefifooffset);
push_edge_fifo(&mut edgefifo, a, c, &mut edgefifooffset);
} else {
let c = if fec != 15 {
last.wrapping_add((fec.wrapping_sub(fec ^ 3)) as u32)
} else {
decode_index(&mut data, last)
};
last = c;
write_triangle(dst, a, b, c);
push_vertex_fifo(&mut vertexfifo, c, &mut vertexfifooffset, None);
push_edge_fifo(&mut edgefifo, c, b, &mut edgefifooffset);
push_edge_fifo(&mut edgefifo, a, c, &mut edgefifooffset);
}
} else {
if codetri < 0xfe {
let codeaux = codeaux_table[codetri & 15];
let feb = codeaux >> 4;
let fec = codeaux & 15;
let a = next;
next += 1;
let bf = vertexfifo[(vertexfifooffset.wrapping_sub(feb as usize)) & 15];
let b = if feb == 0 { next } else { bf };
let feb0 = feb == 0;
next += feb0 as u32;
let cf = vertexfifo[(vertexfifooffset.wrapping_sub(fec as usize)) & 15];
let c = if fec == 0 { next } else { cf };
let fec0 = fec == 0;
next += fec0 as u32;
write_triangle(dst, a, b, c);
push_vertex_fifo(&mut vertexfifo, a, &mut vertexfifooffset, None);
push_vertex_fifo(&mut vertexfifo, b, &mut vertexfifooffset, Some(feb0));
push_vertex_fifo(&mut vertexfifo, c, &mut vertexfifooffset, Some(fec0));
push_edge_fifo(&mut edgefifo, b, a, &mut edgefifooffset);
push_edge_fifo(&mut edgefifo, c, b, &mut edgefifooffset);
push_edge_fifo(&mut edgefifo, a, c, &mut edgefifooffset);
} else {
let codeaux = read_byte(&mut data);
let fea = if codetri == 0xfe { 0 } else { 15 };
let feb = codeaux >> 4;
let fec = codeaux & 15;
if codeaux == 0 {
next = 0;
}
let mut a = if fea == 0 {
let n = next;
next += 1;
n
} else {
0
};
let mut b = if feb == 0 {
let n = next;
next += 1;
n
} else {
vertexfifo[(vertexfifooffset.wrapping_sub(feb as usize)) & 15]
};
let mut c = if fec == 0 {
let n = next;
next += 1;
n
} else {
vertexfifo[(vertexfifooffset.wrapping_sub(fec as usize)) & 15]
};
if fea == 15 {
a = decode_index(&mut data, last);
last = a;
}
if feb == 15 {
b = decode_index(&mut data, last);
last = b;
}
if fec == 15 {
c = decode_index(&mut data, last);
last = c;
}
write_triangle(dst, a, b, c);
push_vertex_fifo(&mut vertexfifo, a, &mut vertexfifooffset, None);
push_vertex_fifo(
&mut vertexfifo,
b,
&mut vertexfifooffset,
Some((feb == 0) | (feb == 15)),
);
push_vertex_fifo(
&mut vertexfifo,
c,
&mut vertexfifooffset,
Some((fec == 0) | (fec == 15)),
);
push_edge_fifo(&mut edgefifo, b, a, &mut edgefifooffset);
push_edge_fifo(&mut edgefifo, c, b, &mut edgefifooffset);
push_edge_fifo(&mut edgefifo, a, c, &mut edgefifooffset);
}
}
}
if data.position() == data_safe_end as u64 {
Ok(())
} else {
Err(DecodeError::ExtraBytes)
}
}
#[cfg(test)]
mod test {
use super::*;
const INDEX_BUFFER: [u32; 12] = [0, 1, 2, 2, 1, 3, 4, 6, 5, 7, 8, 9];
const INDEX_DATA_V0: [u8; 27] = [
0xe0, 0xf0, 0x10, 0xfe, 0xff, 0xf0, 0x0c, 0xff, 0x02, 0x02, 0x02, 0x00, 0x76, 0x87, 0x56, 0x67, 0x78, 0xa9,
0x86, 0x65, 0x89, 0x68, 0x98, 0x01, 0x69, 0x00, 0x00,
];
const INDEX_BUFFER_TRICKY: [u32; 15] = [0, 1, 2, 2, 1, 3, 0, 1, 2, 2, 1, 5, 2, 1, 4];
const INDEX_DATA_V1: [u8; 24] = [
0xe1, 0xf0, 0x10, 0xfe, 0x1f, 0x3d, 0x00, 0x0a, 0x00, 0x76, 0x87, 0x56, 0x67, 0x78, 0xa9, 0x86, 0x65, 0x89,
0x68, 0x98, 0x01, 0x69, 0x00, 0x00,
];
#[test]
fn test_decode_index_v0() {
let mut decoded: [u32; INDEX_BUFFER.len()] = Default::default();
assert!(decode_index_buffer(&mut decoded, &INDEX_DATA_V0).is_ok());
assert_eq!(decoded, INDEX_BUFFER);
}
#[test]
fn test_decode_index_v1() {
let mut decoded: [u32; INDEX_BUFFER_TRICKY.len()] = Default::default();
assert!(decode_index_buffer(&mut decoded, &INDEX_DATA_V1).is_ok());
assert_eq!(decoded, INDEX_BUFFER_TRICKY);
}
#[test]
fn test_decode_index_16() {
let buffer = encode_test_index();
#[derive(Clone, Copy, Default)]
struct U16(u16);
impl From<u32> for U16 {
fn from(index: u32) -> Self {
Self { 0: index as u16 }
}
}
let mut decoded = [U16::default(); INDEX_BUFFER.len()];
assert!(decode_index_buffer(&mut decoded, &buffer).is_ok());
assert!(decoded.iter().enumerate().all(|(i, v)| v.0 as u32 == INDEX_BUFFER[i]));
}
#[test]
fn test_encode_index_memory_safe() {
let mut buffer = encode_test_index();
for i in 0..=buffer.len() {
let result = encode_index_buffer(&mut buffer[0..i], &INDEX_BUFFER, IndexEncodingVersion::default());
if i == buffer.len() {
assert_eq!(result, Some(buffer.len()));
} else {
assert_eq!(result, None);
}
}
}
fn encode_test_index() -> Vec<u8> {
let mut buffer = vec![0; encode_index_buffer_bound(INDEX_BUFFER.len(), 10)];
let written = encode_index_buffer(&mut buffer, &INDEX_BUFFER, IndexEncodingVersion::default()).unwrap();
buffer.resize_with(written, Default::default);
buffer
}
#[test]
fn test_decode_index_memory_safe() {
let buffer = encode_test_index();
let mut decoded: [u32; INDEX_BUFFER.len()] = Default::default();
for i in 0..=buffer.len() {
let result = decode_index_buffer(&mut decoded, &buffer[0..i]);
if i == buffer.len() {
assert!(result.is_ok());
} else {
assert!(result.is_err());
}
}
}
#[test]
fn test_decode_index_reject_extra_bytes() {
let mut buffer = encode_test_index();
buffer.push(0);
let mut decoded: [u32; INDEX_BUFFER.len()] = Default::default();
assert!(decode_index_buffer(&mut decoded, &buffer).is_err());
}
#[test]
fn test_decode_index_reject_malformed_headers() {
let mut buffer = encode_test_index();
buffer[0] = 0;
let mut decoded: [u32; INDEX_BUFFER.len()] = Default::default();
assert!(decode_index_buffer(&mut decoded, &buffer).is_err());
}
#[test]
fn test_decode_index_reject_invalid_version() {
let mut buffer = encode_test_index();
buffer[0] |= 0x0f;
let mut decoded: [u32; INDEX_BUFFER.len()] = Default::default();
assert!(decode_index_buffer(&mut decoded, &buffer).is_err());
}
#[test]
fn test_roundtrip_index_tricky() {
let mut buffer = Vec::new();
buffer.resize_with(
encode_index_buffer_bound(INDEX_BUFFER_TRICKY.len(), 6),
Default::default,
);
let written = encode_index_buffer(&mut buffer, &INDEX_BUFFER_TRICKY, IndexEncodingVersion::default()).unwrap();
buffer.resize_with(written, Default::default);
let mut decoded: [u32; INDEX_BUFFER_TRICKY.len()] = Default::default();
assert!(decode_index_buffer(&mut decoded, &buffer).is_ok());
assert_eq!(decoded, INDEX_BUFFER_TRICKY);
}
#[test]
fn test_encode_index_empty() {
let mut buffer = Vec::new();
buffer.resize_with(encode_index_buffer_bound(0, 0), Default::default);
let written = encode_index_buffer(&mut buffer, &[], IndexEncodingVersion::default()).unwrap();
buffer.resize_with(written, Default::default);
assert!(decode_index_buffer::<u32>(&mut [], &buffer).is_ok());
}
}