Decode spherical-compressed embeddings back to f32 Cartesian coordinates.
(data: &[u8])
| 74 | |
| 75 | /// Decode spherical-compressed embeddings back to f32 Cartesian coordinates. |
| 76 | pub fn decode(data: &[u8]) -> Result<(Vec<f32>, usize, usize), CodecError> { |
| 77 | if data.len() < 9 { |
| 78 | return Err(CodecError::Truncated { |
| 79 | expected: 9, |
| 80 | actual: data.len(), |
| 81 | }); |
| 82 | } |
| 83 | |
| 84 | let dims = u32::from_le_bytes([data[0], data[1], data[2], data[3]]) as usize; |
| 85 | let vectors = u32::from_le_bytes([data[4], data[5], data[6], data[7]]) as usize; |
| 86 | let transform = data[8]; |
| 87 | |
| 88 | if vectors == 0 || dims == 0 { |
| 89 | return Ok((Vec::new(), dims, 0)); |
| 90 | } |
| 91 | |
| 92 | let compressed = &data[9..]; |
| 93 | let raw_bytes = crate::lz4::decode(compressed).map_err(|e| CodecError::DecompressFailed { |
| 94 | detail: format!("spherical lz4: {e}"), |
| 95 | })?; |
| 96 | |
| 97 | let expected_bytes = vectors * dims * 4; |
| 98 | if raw_bytes.len() != expected_bytes { |
| 99 | return Err(CodecError::Corrupt { |
| 100 | detail: format!("expected {expected_bytes} bytes, got {}", raw_bytes.len()), |
| 101 | }); |
| 102 | } |
| 103 | |
| 104 | let floats: Vec<f32> = raw_bytes |
| 105 | .chunks_exact(4) |
| 106 | .map(|c| f32::from_le_bytes([c[0], c[1], c[2], c[3]])) |
| 107 | .collect(); |
| 108 | |
| 109 | if transform == 0 { |
| 110 | // Raw (no transform) — return as-is. |
| 111 | return Ok((floats, dims, vectors)); |
| 112 | } |
| 113 | |
| 114 | // Inverse spherical → Cartesian. |
| 115 | let mut result = Vec::with_capacity(floats.len()); |
| 116 | for v in 0..vectors { |
| 117 | let start = v * dims; |
| 118 | let spherical = &floats[start..start + dims]; |
| 119 | let cartesian = spherical_to_cartesian(spherical); |
| 120 | result.extend_from_slice(&cartesian); |
| 121 | } |
| 122 | |
| 123 | Ok((result, dims, vectors)) |
| 124 | } |
| 125 | |
| 126 | /// Check if embeddings are L2-normalized (suitable for spherical transform). |
| 127 | /// |