mesh_data_tile/
encoder.rs1use std::io::Write;
2
3use crc32fast::hash as crc32;
4use flate2::write::DeflateEncoder;
5
6use crate::common::{
7 encode_no_data_field, expected_payload_length, pack_dtype_endian,
8 validate_tile_id_for_mesh_kind, write_numeric_value,
9};
10use crate::consts::{
11 HEADER_CHECKSUM_INPUT_LENGTH, HEADER_CHECKSUM_OFFSET, MAGIC, OFFSET_BANDS, OFFSET_COLS,
12 OFFSET_COMPRESSED_PAYLOAD_LENGTH, OFFSET_COMPRESSION, OFFSET_DTYPE_ENDIAN, OFFSET_FORMAT_MAJOR,
13 OFFSET_MESH_KIND, OFFSET_NO_DATA_KIND, OFFSET_NO_DATA_VALUE, OFFSET_PAYLOAD_CHECKSUM,
14 OFFSET_ROWS, OFFSET_TILE_ID, OFFSET_UNCOMPRESSED_PAYLOAD_LENGTH, TILE_FIXED_HEADER_LENGTH,
15 TILE_VERSION_MAJOR,
16};
17use crate::{
18 CompressionMode, DType, EncodedTile, Endianness, Result, TileEncodeInput, TileError,
19 TileErrorCode, TileHeader,
20};
21
22pub fn encode_tile(input: TileEncodeInput<'_>) -> Result<EncodedTile> {
23 input.dimensions.validate()?;
24 validate_tile_id_for_mesh_kind(input.tile_id, input.mesh_kind)?;
25
26 let expected_payload_len = expected_payload_length(input.dimensions, input.dtype)?;
27 if input.payload.len() != expected_payload_len {
28 return Err(TileError::new(
29 TileErrorCode::InvalidPayloadLength,
30 format!(
31 "Payload byte length mismatch. expected={expected_payload_len} got={}",
32 input.payload.len()
33 ),
34 ));
35 }
36
37 let payload_crc32 = crc32(input.payload);
38 let compressed_payload = compress_payload(input.compression, input.payload)?;
39 let compressed_payload_len = compressed_payload.len();
40
41 let (no_data_kind, no_data_value_raw) =
42 encode_no_data_field(input.no_data, input.dtype, input.endianness)?;
43
44 let mut header_bytes = [0_u8; TILE_FIXED_HEADER_LENGTH];
45 header_bytes[0..4].copy_from_slice(&MAGIC);
46 header_bytes[OFFSET_FORMAT_MAJOR] = TILE_VERSION_MAJOR;
47 header_bytes[OFFSET_TILE_ID..OFFSET_TILE_ID + 8].copy_from_slice(&input.tile_id.to_le_bytes());
48 header_bytes[OFFSET_MESH_KIND] = input.mesh_kind.code();
49 header_bytes[OFFSET_DTYPE_ENDIAN] = pack_dtype_endian(input.dtype, input.endianness);
50 header_bytes[OFFSET_COMPRESSION] = input.compression.code();
51 header_bytes[OFFSET_ROWS..OFFSET_ROWS + 4]
52 .copy_from_slice(&input.dimensions.rows.to_le_bytes());
53 header_bytes[OFFSET_COLS..OFFSET_COLS + 4]
54 .copy_from_slice(&input.dimensions.cols.to_le_bytes());
55 header_bytes[OFFSET_BANDS] = input.dimensions.bands;
56 header_bytes[OFFSET_NO_DATA_KIND] = no_data_kind;
57 header_bytes[OFFSET_NO_DATA_VALUE..OFFSET_NO_DATA_VALUE + 8]
58 .copy_from_slice(&no_data_value_raw);
59 header_bytes[OFFSET_UNCOMPRESSED_PAYLOAD_LENGTH..OFFSET_UNCOMPRESSED_PAYLOAD_LENGTH + 8]
60 .copy_from_slice(&(input.payload.len() as u64).to_le_bytes());
61 header_bytes[OFFSET_COMPRESSED_PAYLOAD_LENGTH..OFFSET_COMPRESSED_PAYLOAD_LENGTH + 8]
62 .copy_from_slice(&(compressed_payload_len as u64).to_le_bytes());
63 header_bytes[OFFSET_PAYLOAD_CHECKSUM..OFFSET_PAYLOAD_CHECKSUM + 4]
64 .copy_from_slice(&payload_crc32.to_le_bytes());
65 header_bytes[HEADER_CHECKSUM_OFFSET..HEADER_CHECKSUM_OFFSET + 4]
66 .copy_from_slice(&0_u32.to_le_bytes());
67
68 let header_crc32 = crc32(&header_bytes[..HEADER_CHECKSUM_INPUT_LENGTH]);
69 header_bytes[HEADER_CHECKSUM_OFFSET..HEADER_CHECKSUM_OFFSET + 4]
70 .copy_from_slice(&header_crc32.to_le_bytes());
71
72 let mut bytes = Vec::with_capacity(TILE_FIXED_HEADER_LENGTH + compressed_payload_len);
73 bytes.extend_from_slice(&header_bytes);
74 bytes.extend_from_slice(&compressed_payload);
75
76 let header = TileHeader {
77 format_major: TILE_VERSION_MAJOR,
78 tile_id: input.tile_id,
79 mesh_kind: input.mesh_kind,
80 dtype: input.dtype,
81 endianness: input.endianness,
82 compression: input.compression,
83 dimensions: input.dimensions,
84 no_data_kind,
85 no_data_value_raw,
86 no_data: input.no_data,
87 payload_uncompressed_bytes: input.payload.len() as u64,
88 payload_compressed_bytes: compressed_payload_len as u64,
89 payload_crc32,
90 header_crc32,
91 };
92
93 Ok(EncodedTile { bytes, header })
94}
95
96pub fn encode_payload_values(
97 dtype: DType,
98 endianness: Endianness,
99 values: &[f64],
100) -> Result<Vec<u8>> {
101 let value_size = dtype.byte_size();
102 let mut out = vec![0_u8; values.len() * value_size];
103
104 for (idx, value) in values.iter().enumerate() {
105 let start = idx * value_size;
106 let end = start + value_size;
107 write_numeric_value(dtype, endianness, *value, true, &mut out[start..end])?;
108 }
109
110 Ok(out)
111}
112
113fn compress_payload(mode: CompressionMode, payload: &[u8]) -> Result<Vec<u8>> {
114 match mode {
115 CompressionMode::None => Ok(payload.to_vec()),
116 CompressionMode::DeflateRaw => {
117 let mut encoder = DeflateEncoder::new(Vec::new(), flate2::Compression::default());
118 encoder.write_all(payload).map_err(|err| {
119 TileError::new(
120 TileErrorCode::CompressionFailed,
121 format!("Could not compress payload using deflate-raw: {err}"),
122 )
123 })?;
124 encoder.finish().map_err(|err| {
125 TileError::new(
126 TileErrorCode::CompressionFailed,
127 format!("Could not finish deflate-raw compression: {err}"),
128 )
129 })
130 }
131 }
132}