osm_io/osm/pbf/
file_block.rs1use std::fs::File;
2use std::io::{Cursor, Read, Seek, SeekFrom, Write};
3
4use anyhow::{anyhow, Context};
5use flate2::bufread::ZlibDecoder;
6use flate2::Compression;
7use flate2::write::ZlibEncoder;
8use prost::Message;
9
10use crate::osmpbf;
11use crate::osm::model::bounding_box::BoundingBox;
12use crate::osm::model::element::Element;
13use crate::osm::pbf::blob_desc::BlobDesc;
14use crate::osm::pbf::compression_type::CompressionType;
15use crate::osm::pbf::file_block_metadata::FileBlockMetadata;
16use crate::osm::pbf::osm_data::OsmData;
17use crate::osm::pbf::osm_header::OsmHeader;
18use crate::osmpbf::BlobHeader;
19use crate::osmpbf::blob::Data;
20
21#[derive(Debug)]
23pub enum FileBlock {
24 Header {
25 metadata: FileBlockMetadata,
26 header: OsmHeader,
27 },
28 Data {
29 metadata: FileBlockMetadata,
30 data: OsmData,
31 },
32}
33
34impl FileBlock {
35 pub(crate) fn new(index: usize, blob_type: String, data: Vec<u8>) -> Result<FileBlock, anyhow::Error> {
36 let blob_type_str = blob_type.as_str();
37 match blob_type_str {
38 "OSMHeader" => {
39 Ok(
40 FileBlock::Header {
41 metadata: FileBlockMetadata::new(blob_type, index),
42 header: OsmHeader::from_bytes(data)?,
43 }
44 )
45 }
46 "OSMData" => {
47 Ok(
48 FileBlock::Data {
49 metadata: FileBlockMetadata::new(blob_type, index),
50 data: OsmData::new(data)?,
51 }
52 )
53 }
54 _ => {
55 Err(anyhow!("Failed to decode file block"))
56 }
57 }
58 }
59
60 #[allow(dead_code)]
61 pub(crate) fn index(&self) -> usize {
62 match self {
63 FileBlock::Header { metadata, header: _header } => {
64 metadata.index()
65 }
66 FileBlock::Data { metadata, data: _data } => {
67 metadata.index()
68 }
69 }
70 }
71
72 pub(crate) fn from_elements(index: usize, elements: Vec<Element>) -> FileBlock {
73 FileBlock::Data {
74 metadata: FileBlockMetadata::new("OSMData".to_string(), index),
75 data: OsmData::from_elements(elements, None),
76 }
77 }
78
79 #[allow(dead_code)]
80 pub(crate) fn compute_bounding_box(&self) -> Option<BoundingBox> {
81 match self {
82 FileBlock::Header { metadata: _, header } => {
83 header.info().bounding_box().clone()
84 }
85 FileBlock::Data { metadata: _, data } => {
86 data.compute_bounding_box()
87 }
88 }
89 }
90
91 pub(crate) fn from_header(osm_header: OsmHeader) -> FileBlock {
92 FileBlock::Header {
93 metadata: FileBlockMetadata::new("OSMHeader".to_string(), 0),
94 header: osm_header.clone(),
95 }
96 }
97
98 fn zlib_decode(data: Vec<u8>, raw_size: usize) -> Result<Vec<u8>, anyhow::Error> {
99 let mut decoder = ZlibDecoder::new(data.as_slice());
100 let mut decoded = vec![0_u8; raw_size];
101 decoder.read_exact(&mut decoded)?;
102 Ok(decoded)
103 }
104
105 fn zlib_encode(buf: Vec<u8>, compression_level: Compression) -> Result<Vec<u8>, anyhow::Error> {
106 let mut encoder = ZlibEncoder::new(Vec::new(), compression_level);
107 encoder.write_all(buf.as_slice())?;
108 encoder.flush()?;
109 let encoded = encoder.finish()?;
110 Ok(encoded)
111 }
112
113 pub(crate) fn read_blob_data(blob: osmpbf::Blob) -> Result<Vec<u8>, anyhow::Error> {
114 match blob.data {
115 None => {
116 Err(
117 anyhow!("Input file too short")
118 )
119 }
120 Some(data) => {
121 match data {
122 Data::Raw(_) => {
123 Err(
124 anyhow!("Raw data type not implemented")
126 )
127 }
128 Data::ZlibData(zlib_data) => {
129 FileBlock::zlib_decode(zlib_data, blob.raw_size.unwrap() as usize)
131 }
132 Data::LzmaData(_) => {
133 Err(
134 anyhow!("Lzma data type not implemented")
136 )
137 }
138 Data::ObsoleteBzip2Data(_) => {
139 Err(
140 anyhow!("Obsolete Bzip data type not implemented")
141 )
142 }
143 Data::Lz4Data(_) => {
144 Err(
145 anyhow!("Lz4 data type not implemented")
147 )
148 }
149 Data::ZstdData(_) => {
150 Err(
151 anyhow!("Zstd data type not implemented")
152 )
153 }
154 }
155 }
156 }
157 }
158
159 pub(crate) fn from_blob_desc(blob_desc: &BlobDesc) -> Result<FileBlock, anyhow::Error> {
160 let mut file = File::open(blob_desc.path()).with_context(
161 || anyhow!("Failed to open {:?} for reading", blob_desc.path())
162 )?;
163 file.seek(SeekFrom::Start(blob_desc.start())).with_context(
164 || anyhow!("Failed seek to {} in {:?} ", blob_desc.start(), blob_desc.path())
165 )?;
166 let mut blob_buffer = vec![0; blob_desc.length() as usize];
167 file.read_exact(&mut blob_buffer).ok().with_context(
168 || anyhow!("Failed to read {} bytes from {:?} ", blob_desc.length(), blob_desc.path())
169 )?;
170 Self::deserialize(blob_desc, &mut blob_buffer)
171 }
172
173 pub(crate) fn serialize(file_block: &FileBlock, compression: CompressionType) -> Result<(Vec<u8>, Vec<u8>), anyhow::Error> {
174 let (blob_type, compression_level, block_data) = match file_block {
175 FileBlock::Header { metadata: _, header } => {
176 ("OSMHeader".to_string(), Compression::none(), header.serialize()?)
177 }
178 FileBlock::Data { metadata: _, data } => {
179 ("OSMData".to_string(), Compression::default(), data.serialize()?)
180 }
181 };
182
183 let mut raw_size = None;
184 let mut data = None;
185 if !block_data.is_empty() {
186 raw_size = Some(block_data.len() as i32);
187 data = match compression {
188 CompressionType::Uncompressed => {
189 Some(Data::Raw(block_data))
190 }
191 CompressionType::Zlib => {
192 let encoded = Self::zlib_encode(block_data, compression_level)?;
193 Some(Data::ZlibData(encoded))
194 }
195 };
196 }
197
198 let blob = osmpbf::Blob {
199 raw_size,
200 data,
201 };
202 let body = blob.encode_to_vec();
203
204 let blob_header = BlobHeader {
205 r#type: blob_type,
206 indexdata: None,
207 datasize: body.len() as i32,
208 };
209
210
211 let header = blob_header.encode_to_vec();
212
213 Ok((header, body))
214 }
215
216 fn deserialize(blob_desc: &BlobDesc, blob_buffer: &mut Vec<u8>) -> Result<FileBlock, anyhow::Error> {
217 let protobuf_blob = osmpbf::Blob::decode(&mut Cursor::new(blob_buffer)).with_context(
219 || anyhow!("Failed to decode a message from blob {} from {:?}", blob_desc.index(), blob_desc.path())
220 )?;
221 let data = FileBlock::read_blob_data(protobuf_blob)?;
222 FileBlock::new(blob_desc.index(), blob_desc.t(), data)
223 }
224
225 #[allow(dead_code)]
226 pub(crate) fn metadata(&self) -> &FileBlockMetadata {
227 match self {
228 FileBlock::Header { metadata, header: _ } => {
229 metadata
230 }
231 FileBlock::Data { metadata, data: _ } => {
232 metadata
233 }
234 }
235 }
236
237 pub(crate) fn as_osm_header(&self) -> Result<&OsmHeader, anyhow::Error> {
238 match self {
239 FileBlock::Header { header, .. } => {
240 Ok(header)
241 }
242 FileBlock::Data { .. } => {
243 Err(anyhow!("Not an OSMHeader"))
244 }
245 }
246 }
247 pub(crate) fn is_osm_header(&self) -> bool {
248 match self {
249 FileBlock::Header { header: _, .. } => {
250 true
251 }
252 FileBlock::Data { .. } => {
253 false
254 }
255 }
256 }
257
258 pub(crate) fn is_osm_data(&self) -> bool {
259 !self.is_osm_header()
260 }
261
262 #[allow(dead_code)]
263 pub(crate) fn as_osm_data(&self) -> Result<&OsmData, anyhow::Error> {
264 match self {
265 FileBlock::Header { .. } => {
266 Err(anyhow!("Not an OSMData"))
267 }
268 FileBlock::Data { data, .. } => {
269 Ok(data)
270 }
271 }
272 }
273
274 #[allow(dead_code)]
275 pub(crate) fn elements(&self) -> &Vec<Element> {
276 self.as_osm_data().unwrap().elements()
277 }
278
279 pub(crate) fn take_elements(&mut self) -> Vec<Element> {
280 match self {
281 FileBlock::Header { .. } => {
282 panic!("Not a Data variant")
283 }
284 FileBlock::Data { data, .. } => {
285 data.take_elements()
286 }
287 }
288 }
289}
290
291impl Default for FileBlock {
292 fn default() -> Self {
293 FileBlock::Data { metadata: Default::default(), data: Default::default() }
294 }
295}