1use {
12 crate::{
13 resource::Resource,
14 serialization::{BlobInteriorPadding, BlobSectionField, ResourceField, HEADER_V3},
15 },
16 anyhow::{anyhow, Context, Result},
17 byteorder::{LittleEndian, WriteBytesExt},
18 std::{collections::BTreeMap, io::Write, path::Path},
19};
20
21#[cfg(unix)]
22use std::os::unix::ffi::OsStrExt;
23#[cfg(windows)]
24use std::os::windows::ffi::OsStrExt;
25
26#[cfg(unix)]
27fn path_bytes_length(p: &Path) -> usize {
28 p.as_os_str().as_bytes().len()
29}
30
31#[cfg(unix)]
32fn path_to_bytes(p: &Path) -> Vec<u8> {
33 p.as_os_str().as_bytes().to_vec()
34}
35
36#[cfg(windows)]
37fn path_bytes_length(p: &Path) -> usize {
38 p.as_os_str().encode_wide().count() * 2
39}
40
41#[cfg(windows)]
42fn path_to_bytes(p: &Path) -> Vec<u8> {
43 let mut source = p.as_os_str().encode_wide().collect::<Vec<u16>>();
44
45 let ptr = source.as_mut_ptr() as *mut u8;
46 let len = source.len() * std::mem::size_of::<u16>();
47 let capacity = source.capacity() * std::mem::size_of::<u16>();
48 unsafe {
49 std::mem::forget(source);
50 Vec::from_raw_parts(ptr, len, capacity)
51 }
52}
53
54#[derive(Debug)]
55struct BlobSection {
56 resource_field: ResourceField,
57 raw_payload_length: usize,
58 interior_padding: Option<BlobInteriorPadding>,
59}
60
61impl BlobSection {
62 pub fn index_v1_length(&self) -> usize {
64 let mut index = 1;
66
67 index += 2;
69
70 index += 9;
72
73 if self.interior_padding.is_some() {
74 index += 2;
76 }
77
78 index += 1;
80
81 index
82 }
83
84 pub fn write_index_v1<W: Write>(&self, dest: &mut W) -> Result<()> {
85 dest.write_u8(BlobSectionField::StartOfEntry.into())
86 .context("writing start of index entry")?;
87
88 dest.write_u8(BlobSectionField::ResourceFieldType.into())
89 .context("writing resource field type field")?;
90 dest.write_u8(self.resource_field.into())
91 .context("writing resource field type value")?;
92
93 dest.write_u8(BlobSectionField::RawPayloadLength.into())
94 .context("writing raw payload length field")?;
95 dest.write_u64::<LittleEndian>(self.raw_payload_length as u64)
96 .context("writing raw payload length")?;
97
98 if let Some(padding) = &self.interior_padding {
99 dest.write_u8(BlobSectionField::InteriorPadding.into())
100 .context("writing interior padding field")?;
101 dest.write_u8(padding.into())
102 .context("writing interior padding value")?;
103 }
104
105 dest.write_u8(BlobSectionField::EndOfEntry.into())
106 .context("writing end of index entry")?;
107
108 Ok(())
109 }
110}
111
112impl<'a, X: Clone + 'a> Resource<'a, X>
113where
114 [X]: ToOwned<Owned = Vec<X>>,
115{
116 pub fn is_meaningful(&self) -> bool {
120 self.is_python_package
121 || self.is_python_namespace_package
122 || self.in_memory_source.is_some()
123 || self.in_memory_bytecode.is_some()
124 || self.in_memory_bytecode_opt1.is_some()
125 || self.in_memory_bytecode_opt2.is_some()
126 || self.in_memory_extension_module_shared_library.is_some()
127 || self.in_memory_package_resources.is_some()
128 || self.in_memory_distribution_resources.is_some()
129 || self.in_memory_shared_library.is_some()
130 || self.relative_path_module_source.is_some()
131 || self.relative_path_module_bytecode.is_some()
132 || self.relative_path_module_bytecode_opt1.is_some()
133 || self.relative_path_module_bytecode_opt2.is_some()
134 || self.relative_path_extension_module_shared_library.is_some()
135 || self.relative_path_package_resources.is_some()
136 || self.relative_path_distribution_resources.is_some()
137 || self.file_data_embedded.is_some()
138 || self.file_data_utf8_relative_path.is_some()
139 }
140
141 pub fn index_v1_length(&self) -> usize {
143 let mut index = 1;
145
146 index += 3;
148
149 if self.is_python_package {
150 index += 1;
151 }
152
153 if self.is_python_namespace_package {
154 index += 1;
155 }
156
157 if self.in_memory_source.is_some() {
158 index += 5;
159 }
160
161 if self.in_memory_bytecode.is_some() {
162 index += 5;
163 }
164
165 if self.in_memory_bytecode_opt1.is_some() {
166 index += 5;
167 }
168
169 if self.in_memory_bytecode_opt2.is_some() {
170 index += 5;
171 }
172
173 if self.in_memory_extension_module_shared_library.is_some() {
174 index += 5;
175 }
176
177 if let Some(resources) = &self.in_memory_package_resources {
178 index += 5;
179
180 index += 10 * resources.len();
182 }
183
184 if let Some(metadata) = &self.in_memory_distribution_resources {
185 index += 5;
186 index += 10 * metadata.len();
188 }
189
190 if self.in_memory_shared_library.is_some() {
191 index += 9;
192 }
193
194 if let Some(names) = &self.shared_library_dependency_names {
195 index += 3 + 2 * names.len();
196 }
197
198 if self.relative_path_module_source.is_some() {
199 index += 5;
200 }
201
202 if self.relative_path_module_bytecode.is_some() {
203 index += 5;
204 }
205
206 if self.relative_path_module_bytecode_opt1.is_some() {
207 index += 5;
208 }
209
210 if self.relative_path_module_bytecode_opt2.is_some() {
211 index += 5;
212 }
213
214 if self.relative_path_extension_module_shared_library.is_some() {
215 index += 5;
216 }
217
218 if let Some(resources) = &self.relative_path_package_resources {
219 index += 5;
220
221 index += 6 * resources.len();
223 }
224
225 if let Some(metadata) = &self.relative_path_distribution_resources {
226 index += 5;
227
228 index += 6 * metadata.len();
229 }
230
231 if self.is_python_module {
232 index += 1;
233 }
234
235 if self.is_python_builtin_extension_module {
236 index += 1;
237 }
238
239 if self.is_python_frozen_module {
240 index += 1;
241 }
242
243 if self.is_python_extension_module {
244 index += 1;
245 }
246
247 if self.is_shared_library {
248 index += 1;
249 }
250
251 if self.is_utf8_filename_data {
252 index += 1;
253 }
254
255 if self.file_executable {
256 index += 1;
257 }
258
259 if self.file_data_embedded.is_some() {
260 index += 9;
261 }
262
263 if self.file_data_utf8_relative_path.is_some() {
264 index += 5;
265 }
266
267 index += 1;
269
270 index
271 }
272
273 pub fn field_blob_length(&self, field: ResourceField) -> usize {
277 match field {
278 ResourceField::EndOfIndex => 0,
279 ResourceField::StartOfEntry => 0,
280 ResourceField::EndOfEntry => 0,
281 ResourceField::Name => self.name.as_bytes().len(),
282 ResourceField::IsPythonPackage => 0,
283 ResourceField::IsPythonNamespacePackage => 0,
284 ResourceField::InMemorySource => {
285 if let Some(source) = &self.in_memory_source {
286 source.len()
287 } else {
288 0
289 }
290 }
291 ResourceField::InMemoryBytecode => {
292 if let Some(bytecode) = &self.in_memory_bytecode {
293 bytecode.len()
294 } else {
295 0
296 }
297 }
298 ResourceField::InMemoryBytecodeOpt1 => {
299 if let Some(bytecode) = &self.in_memory_bytecode_opt1 {
300 bytecode.len()
301 } else {
302 0
303 }
304 }
305 ResourceField::InMemoryBytecodeOpt2 => {
306 if let Some(bytecode) = &self.in_memory_bytecode_opt2 {
307 bytecode.len()
308 } else {
309 0
310 }
311 }
312 ResourceField::InMemoryExtensionModuleSharedLibrary => {
313 if let Some(library) = &self.in_memory_extension_module_shared_library {
314 library.len()
315 } else {
316 0
317 }
318 }
319 ResourceField::InMemoryResourcesData => {
320 if let Some(resources) = &self.in_memory_package_resources {
321 resources
322 .iter()
323 .map(|(key, value)| key.as_bytes().len() + value.len())
324 .sum()
325 } else {
326 0
327 }
328 }
329 ResourceField::InMemoryDistributionResource => {
330 if let Some(metadata) = &self.in_memory_distribution_resources {
331 metadata
332 .iter()
333 .map(|(key, value)| key.as_bytes().len() + value.len())
334 .sum()
335 } else {
336 0
337 }
338 }
339 ResourceField::InMemorySharedLibrary => {
340 if let Some(library) = &self.in_memory_shared_library {
341 library.len()
342 } else {
343 0
344 }
345 }
346 ResourceField::SharedLibraryDependencyNames => {
347 if let Some(names) = &self.shared_library_dependency_names {
348 names.iter().map(|s| s.as_bytes().len()).sum()
349 } else {
350 0
351 }
352 }
353 ResourceField::RelativeFilesystemModuleSource => {
354 if let Some(path) = &self.relative_path_module_source {
355 path_bytes_length(path)
356 } else {
357 0
358 }
359 }
360 ResourceField::RelativeFilesystemModuleBytecode => {
361 if let Some(path) = &self.relative_path_module_bytecode {
362 path_bytes_length(path)
363 } else {
364 0
365 }
366 }
367 ResourceField::RelativeFilesystemModuleBytecodeOpt1 => {
368 if let Some(path) = &self.relative_path_module_bytecode_opt1 {
369 path_bytes_length(path)
370 } else {
371 0
372 }
373 }
374 ResourceField::RelativeFilesystemModuleBytecodeOpt2 => {
375 if let Some(path) = &self.relative_path_module_bytecode_opt2 {
376 path_bytes_length(path)
377 } else {
378 0
379 }
380 }
381 ResourceField::RelativeFilesystemExtensionModuleSharedLibrary => {
382 if let Some(path) = &self.relative_path_extension_module_shared_library {
383 path_bytes_length(path)
384 } else {
385 0
386 }
387 }
388 ResourceField::RelativeFilesystemPackageResources => {
389 if let Some(resources) = &self.relative_path_package_resources {
390 resources
391 .iter()
392 .map(|(key, value)| key.as_bytes().len() + path_bytes_length(value))
393 .sum()
394 } else {
395 0
396 }
397 }
398 ResourceField::RelativeFilesystemDistributionResource => {
399 if let Some(metadata) = &self.relative_path_distribution_resources {
400 metadata
401 .iter()
402 .map(|(key, value)| key.as_bytes().len() + path_bytes_length(value))
403 .sum()
404 } else {
405 0
406 }
407 }
408 ResourceField::IsPythonModule => 0,
409 ResourceField::IsPythonBuiltinExtensionModule => 0,
410 ResourceField::IsPythonFrozenModule => 0,
411 ResourceField::IsPythonExtensionModule => 0,
412 ResourceField::IsSharedLibrary => 0,
413 ResourceField::IsUtf8FilenameData => 0,
414 ResourceField::FileExecutable => 0,
415 ResourceField::FileDataEmbedded => {
416 if let Some(data) = &self.file_data_embedded {
417 data.len()
418 } else {
419 0
420 }
421 }
422 ResourceField::FileDataUtf8RelativePath => {
423 if let Some(path) = &self.file_data_utf8_relative_path {
424 path.as_bytes().len()
425 } else {
426 0
427 }
428 }
429 }
430 }
431
432 pub fn field_blob_interior_padding_length(
434 &self,
435 field: ResourceField,
436 padding: BlobInteriorPadding,
437 ) -> usize {
438 let elements_count = match field {
439 ResourceField::EndOfIndex => 0,
440 ResourceField::StartOfEntry => 0,
441 ResourceField::EndOfEntry => 0,
442 ResourceField::Name => 1,
443 ResourceField::IsPythonPackage => 0,
444 ResourceField::IsPythonNamespacePackage => 0,
445 ResourceField::InMemorySource => {
446 if self.in_memory_source.is_some() {
447 1
448 } else {
449 0
450 }
451 }
452 ResourceField::InMemoryBytecode => {
453 if self.in_memory_bytecode.is_some() {
454 1
455 } else {
456 0
457 }
458 }
459 ResourceField::InMemoryBytecodeOpt1 => {
460 if self.in_memory_bytecode_opt1.is_some() {
461 1
462 } else {
463 0
464 }
465 }
466 ResourceField::InMemoryBytecodeOpt2 => {
467 if self.in_memory_bytecode_opt2.is_some() {
468 1
469 } else {
470 0
471 }
472 }
473 ResourceField::InMemoryExtensionModuleSharedLibrary => {
474 if self.in_memory_extension_module_shared_library.is_some() {
475 1
476 } else {
477 0
478 }
479 }
480 ResourceField::InMemoryResourcesData => {
481 if let Some(resources) = &self.in_memory_package_resources {
482 resources.len() * 2
483 } else {
484 0
485 }
486 }
487 ResourceField::InMemoryDistributionResource => {
488 if let Some(metadata) = &self.in_memory_distribution_resources {
489 metadata.len() * 2
490 } else {
491 0
492 }
493 }
494 ResourceField::InMemorySharedLibrary => {
495 if self.in_memory_shared_library.is_some() {
496 1
497 } else {
498 0
499 }
500 }
501 ResourceField::SharedLibraryDependencyNames => {
502 if let Some(names) = &self.shared_library_dependency_names {
503 names.len()
504 } else {
505 0
506 }
507 }
508 ResourceField::RelativeFilesystemModuleSource => {
509 if self.relative_path_module_source.is_some() {
510 1
511 } else {
512 0
513 }
514 }
515 ResourceField::RelativeFilesystemModuleBytecode => {
516 if self.relative_path_module_bytecode.is_some() {
517 1
518 } else {
519 0
520 }
521 }
522 ResourceField::RelativeFilesystemModuleBytecodeOpt1 => {
523 if self.relative_path_module_bytecode_opt1.is_some() {
524 1
525 } else {
526 0
527 }
528 }
529 ResourceField::RelativeFilesystemModuleBytecodeOpt2 => {
530 if self.relative_path_module_bytecode_opt2.is_some() {
531 1
532 } else {
533 0
534 }
535 }
536 ResourceField::RelativeFilesystemExtensionModuleSharedLibrary => {
537 if self.relative_path_extension_module_shared_library.is_some() {
538 1
539 } else {
540 0
541 }
542 }
543 ResourceField::RelativeFilesystemPackageResources => {
544 if let Some(resources) = &self.relative_path_package_resources {
545 resources.len() * 2
546 } else {
547 0
548 }
549 }
550 ResourceField::RelativeFilesystemDistributionResource => {
551 if let Some(resources) = &self.relative_path_distribution_resources {
552 resources.len() * 2
553 } else {
554 0
555 }
556 }
557 ResourceField::IsPythonModule => 0,
558 ResourceField::IsPythonBuiltinExtensionModule => 0,
559 ResourceField::IsPythonFrozenModule => 0,
560 ResourceField::IsPythonExtensionModule => 0,
561 ResourceField::IsSharedLibrary => 0,
562 ResourceField::IsUtf8FilenameData => 0,
563 ResourceField::FileExecutable => 0,
564 ResourceField::FileDataEmbedded => {
565 if self.file_data_embedded.is_some() {
566 1
567 } else {
568 0
569 }
570 }
571 ResourceField::FileDataUtf8RelativePath => {
572 if self.file_data_utf8_relative_path.is_some() {
573 1
574 } else {
575 0
576 }
577 }
578 };
579
580 let overhead = match padding {
581 BlobInteriorPadding::None => 0,
582 BlobInteriorPadding::Null => 1,
583 };
584
585 elements_count * overhead
586 }
587
588 pub fn write_index_v1<W: Write>(&self, dest: &mut W) -> Result<()> {
590 let name_len =
591 u16::try_from(self.name.as_bytes().len()).context("converting name to u16")?;
592
593 dest.write_u8(ResourceField::StartOfEntry.into())
594 .context("writing start of index entry")?;
595
596 dest.write_u8(ResourceField::Name.into())
597 .context("writing resource name field")?;
598
599 dest.write_u16::<LittleEndian>(name_len)
600 .context("writing resource name length")?;
601
602 if self.is_python_package {
603 dest.write_u8(ResourceField::IsPythonPackage.into())
604 .context("writing is_package field")?;
605 }
606
607 if self.is_python_namespace_package {
608 dest.write_u8(ResourceField::IsPythonNamespacePackage.into())
609 .context("writing is_namespace field")?;
610 }
611
612 if let Some(source) = &self.in_memory_source {
613 let l =
614 u32::try_from(source.len()).context("converting in-memory source length to u32")?;
615 dest.write_u8(ResourceField::InMemorySource.into())
616 .context("writing in-memory source length field")?;
617 dest.write_u32::<LittleEndian>(l)
618 .context("writing in-memory source length")?;
619 }
620
621 if let Some(bytecode) = &self.in_memory_bytecode {
622 let l = u32::try_from(bytecode.len())
623 .context("converting in-memory bytecode length to u32")?;
624 dest.write_u8(ResourceField::InMemoryBytecode.into())
625 .context("writing in-memory bytecode length field")?;
626 dest.write_u32::<LittleEndian>(l)
627 .context("writing in-memory bytecode length")?;
628 }
629
630 if let Some(bytecode) = &self.in_memory_bytecode_opt1 {
631 let l = u32::try_from(bytecode.len())
632 .context("converting in-memory bytecode opt 1 length to u32")?;
633 dest.write_u8(ResourceField::InMemoryBytecodeOpt1.into())
634 .context("writing in-memory bytecode opt 1 length field")?;
635 dest.write_u32::<LittleEndian>(l)
636 .context("writing in-memory bytecode opt 1 length")?;
637 }
638
639 if let Some(bytecode) = &self.in_memory_bytecode_opt2 {
640 let l = u32::try_from(bytecode.len())
641 .context("converting in-memory bytecode opt 2 length to u32")?;
642 dest.write_u8(ResourceField::InMemoryBytecodeOpt2.into())
643 .context("writing in-memory bytecode opt 2 field")?;
644 dest.write_u32::<LittleEndian>(l)
645 .context("writing in-memory bytecode opt 2 length")?;
646 }
647
648 if let Some(library) = &self.in_memory_extension_module_shared_library {
649 let l = u32::try_from(library.len())
650 .context("converting in-memory library length to u32")?;
651 dest.write_u8(ResourceField::InMemoryExtensionModuleSharedLibrary.into())
652 .context("writing in-memory extension module shared library field")?;
653 dest.write_u32::<LittleEndian>(l)
654 .context("writing in-memory extension module shared library length")?;
655 }
656
657 if let Some(resources) = &self.in_memory_package_resources {
658 let l = u32::try_from(resources.len())
659 .context("converting in-memory resources data length to u32")?;
660 dest.write_u8(ResourceField::InMemoryResourcesData.into())
661 .context("writing in-memory resources field")?;
662 dest.write_u32::<LittleEndian>(l)
663 .context("writing in-memory resources data length")?;
664
665 for (name, value) in resources.iter() {
666 let name_length = u16::try_from(name.as_bytes().len())
667 .context("converting resource name length to u16")?;
668 dest.write_u16::<LittleEndian>(name_length)
669 .context("writing resource name length")?;
670 dest.write_u64::<LittleEndian>(value.len() as u64)
671 .context("writing resource data length")?;
672 }
673 }
674
675 if let Some(metadata) = &self.in_memory_distribution_resources {
676 let l = u32::try_from(metadata.len())
677 .context("converting in-memory distribution metadata length to u32")?;
678 dest.write_u8(ResourceField::InMemoryDistributionResource.into())
679 .context("writing in-memory package distribution field")?;
680 dest.write_u32::<LittleEndian>(l)
681 .context("writing in-memory package distribution length")?;
682
683 for (name, value) in metadata {
684 let name_length = u16::try_from(name.as_bytes().len())
685 .context("converting distribution name length to u16")?;
686 dest.write_u16::<LittleEndian>(name_length)
687 .context("writing distribution name length")?;
688 dest.write_u64::<LittleEndian>(value.len() as u64)
689 .context("writing distribution data length")?;
690 }
691 }
692
693 if let Some(library) = &self.in_memory_shared_library {
694 let l = u64::try_from(library.len())
695 .context("converting in-memory shared library length to u64")?;
696 dest.write_u8(ResourceField::InMemorySharedLibrary.into())
697 .context("writing in-memory shared library field")?;
698 dest.write_u64::<LittleEndian>(l)
699 .context("writing in-memory shared library length")?;
700 }
701
702 if let Some(names) = &self.shared_library_dependency_names {
703 let l = u16::try_from(names.len())
704 .context("converting shared library dependency names to u16")?;
705 dest.write_u8(ResourceField::SharedLibraryDependencyNames.into())
706 .context("writing shared library dependency names field")?;
707 dest.write_u16::<LittleEndian>(l)
708 .context("writing shared library dependency names length")?;
709
710 for name in names {
711 let name_length = u16::try_from(name.as_bytes().len())
712 .context("converting shared library dependency name length to u16")?;
713 dest.write_u16::<LittleEndian>(name_length)
714 .context("writing shared library dependency name length")?;
715 }
716 }
717
718 if let Some(path) = &self.relative_path_module_source {
719 let l = u32::try_from(path_bytes_length(path))
720 .context("converting module source relative path length to u32")?;
721 dest.write_u8(ResourceField::RelativeFilesystemModuleSource.into())
722 .context("writing relative path module source field")?;
723 dest.write_u32::<LittleEndian>(l)
724 .context("writing relative path module source length")?;
725 }
726
727 if let Some(path) = &self.relative_path_module_bytecode {
728 let l = u32::try_from(path_bytes_length(path))
729 .context("converting module bytecode relative path to u32")?;
730 dest.write_u8(ResourceField::RelativeFilesystemModuleBytecode.into())
731 .context("writing relative path module bytecode field")?;
732 dest.write_u32::<LittleEndian>(l)
733 .context("writing relative path module bytecode length")?;
734 }
735
736 if let Some(path) = &self.relative_path_module_bytecode_opt1 {
737 let l = u32::try_from(path_bytes_length(path))
738 .context("converting module bytecode opt1 relative path to u32")?;
739 dest.write_u8(ResourceField::RelativeFilesystemModuleBytecodeOpt1.into())
740 .context("writing relative path module bytecode opt1 field")?;
741 dest.write_u32::<LittleEndian>(l)
742 .context("writing relative path module bytecode opt1 length")?;
743 }
744
745 if let Some(path) = &self.relative_path_module_bytecode_opt2 {
746 let l = u32::try_from(path_bytes_length(path))
747 .context("converting module bytecode opt2 relative path to u32")?;
748 dest.write_u8(ResourceField::RelativeFilesystemModuleBytecodeOpt2.into())
749 .context("writing relative path module bytecode opt2 field")?;
750 dest.write_u32::<LittleEndian>(l)
751 .context("writing relative path module bytecode opt2 length")?;
752 }
753
754 if let Some(path) = &self.relative_path_extension_module_shared_library {
755 let l = u32::try_from(path_bytes_length(path))
756 .context("converting extension module shared library relative path to u32")?;
757 dest.write_u8(ResourceField::RelativeFilesystemExtensionModuleSharedLibrary.into())
758 .context("writing relative path extension module shared library field")?;
759 dest.write_u32::<LittleEndian>(l)
760 .context("writing relative path extension module shared library length")?;
761 }
762
763 if let Some(resources) = &self.relative_path_package_resources {
764 let l = u32::try_from(resources.len())
765 .context("converting relative path resources data length to u32")?;
766 dest.write_u8(ResourceField::RelativeFilesystemPackageResources.into())
767 .context("writing relative path resources resources field")?;
768 dest.write_u32::<LittleEndian>(l)
769 .context("writing relative path resources resources data length")?;
770
771 for (name, path) in resources.iter() {
772 let name_length = u16::try_from(name.as_bytes().len())
773 .context("converting resource name length to u16")?;
774 let path_length = u32::try_from(path_bytes_length(path))
775 .context("converting resource path length to u32")?;
776 dest.write_u16::<LittleEndian>(name_length)
777 .context("writing resource name length")?;
778 dest.write_u32::<LittleEndian>(path_length)
779 .context("writing resource path length")?;
780 }
781 }
782
783 if let Some(metadata) = &self.relative_path_distribution_resources {
784 let l = u32::try_from(metadata.len())
785 .context("converting relative path distribution length to u32")?;
786 dest.write_u8(ResourceField::RelativeFilesystemDistributionResource.into())
787 .context("writing relative path resources resources field")?;
788 dest.write_u32::<LittleEndian>(l)
789 .context("writing relative path distribution data length")?;
790
791 for (name, path) in metadata.iter() {
792 let name_length = u16::try_from(name.as_bytes().len())
793 .context("converting resource name length to u16")?;
794 let path_length = u32::try_from(path_bytes_length(path))
795 .context("converting resource path length to u32")?;
796 dest.write_u16::<LittleEndian>(name_length)
797 .context("writing resource name length")?;
798 dest.write_u32::<LittleEndian>(path_length)
799 .context("writing resource path length")?;
800 }
801 }
802
803 if self.is_python_module {
804 dest.write_u8(ResourceField::IsPythonModule.into())
805 .context("writing is_module field")?;
806 }
807
808 if self.is_python_builtin_extension_module {
809 dest.write_u8(ResourceField::IsPythonBuiltinExtensionModule.into())
810 .context("writing is_builtin_extension_module field")?;
811 }
812
813 if self.is_python_frozen_module {
814 dest.write_u8(ResourceField::IsPythonFrozenModule.into())
815 .context("writing is_frozen_module field")?;
816 }
817
818 if self.is_python_extension_module {
819 dest.write_u8(ResourceField::IsPythonExtensionModule.into())
820 .context("writing is_extension_module field")?;
821 }
822
823 if self.is_shared_library {
824 dest.write_u8(ResourceField::IsSharedLibrary.into())
825 .context("writing is_shared_library field")?;
826 }
827
828 if self.is_utf8_filename_data {
829 dest.write_u8(ResourceField::IsUtf8FilenameData.into())
830 .context("writing is_utf8_filename_data field")?;
831 }
832
833 if self.file_executable {
834 dest.write_u8(ResourceField::FileExecutable.into())
835 .context("writing file_executable field")?;
836 }
837
838 if let Some(data) = &self.file_data_embedded {
839 let l =
840 u64::try_from(data.len()).context("converting embedded file data length to u64")?;
841 dest.write_u8(ResourceField::FileDataEmbedded.into())
842 .context("writing file_data_embedded field")?;
843 dest.write_u64::<LittleEndian>(l)
844 .context("writing file_data_embedded length")?;
845 }
846
847 if let Some(path) = &self.file_data_utf8_relative_path {
848 let l = u32::try_from(path.as_bytes().len())
849 .context("converting embedded file data relative path length to u32")?;
850 dest.write_u8(ResourceField::FileDataUtf8RelativePath.into())
851 .context("writing file_data_utf8_relative_path field")?;
852 dest.write_u32::<LittleEndian>(l)
853 .context("writing file_data_utf_relative_path field")?;
854 }
855
856 dest.write_u8(ResourceField::EndOfEntry.into())
857 .map_err(|_| anyhow!("error writing end of index entry"))?;
858
859 Ok(())
860 }
861}
862
863#[allow(clippy::cognitive_complexity)]
865pub fn write_packed_resources_v3<'a, T: AsRef<Resource<'a, u8>>, W: Write>(
866 resources: &[T],
867 dest: &mut W,
868 interior_padding: Option<BlobInteriorPadding>,
869) -> Result<()> {
870 let mut blob_sections = BTreeMap::new();
871
872 let mut blob_section_count = 0;
873 let mut blob_index_length = 1;
875
876 let mut resource_index_length = 1;
878
879 let process_field = |blob_sections: &mut BTreeMap<ResourceField, BlobSection>,
880 resource: &Resource<u8>,
881 field: ResourceField| {
882 let padding = match &interior_padding {
883 Some(padding) => *padding,
884 None => BlobInteriorPadding::None,
885 };
886
887 let l = resource.field_blob_length(field)
888 + resource.field_blob_interior_padding_length(field, padding);
889 if l > 0 {
890 blob_sections
891 .entry(field)
892 .or_insert_with(|| BlobSection {
893 resource_field: field,
894 raw_payload_length: 0,
895 interior_padding,
896 })
897 .raw_payload_length += l;
898 }
899 };
900
901 let add_interior_padding = |dest: &mut W| -> Result<()> {
902 if interior_padding == Some(BlobInteriorPadding::Null) {
903 dest.write_all(b"\0")?;
904 }
905
906 Ok(())
907 };
908
909 for resource in resources {
910 let resource = resource.as_ref();
911 resource_index_length += resource.index_v1_length();
912
913 process_field(&mut blob_sections, resource, ResourceField::Name);
914 process_field(&mut blob_sections, resource, ResourceField::InMemorySource);
915 process_field(
916 &mut blob_sections,
917 resource,
918 ResourceField::InMemoryBytecode,
919 );
920 process_field(
921 &mut blob_sections,
922 resource,
923 ResourceField::InMemoryBytecodeOpt1,
924 );
925 process_field(
926 &mut blob_sections,
927 resource,
928 ResourceField::InMemoryBytecodeOpt2,
929 );
930 process_field(
931 &mut blob_sections,
932 resource,
933 ResourceField::InMemoryExtensionModuleSharedLibrary,
934 );
935 process_field(
936 &mut blob_sections,
937 resource,
938 ResourceField::InMemoryResourcesData,
939 );
940 process_field(
941 &mut blob_sections,
942 resource,
943 ResourceField::InMemoryDistributionResource,
944 );
945 process_field(
946 &mut blob_sections,
947 resource,
948 ResourceField::InMemorySharedLibrary,
949 );
950 process_field(
951 &mut blob_sections,
952 resource,
953 ResourceField::SharedLibraryDependencyNames,
954 );
955 process_field(
956 &mut blob_sections,
957 resource,
958 ResourceField::RelativeFilesystemModuleSource,
959 );
960 process_field(
961 &mut blob_sections,
962 resource,
963 ResourceField::RelativeFilesystemModuleBytecode,
964 );
965 process_field(
966 &mut blob_sections,
967 resource,
968 ResourceField::RelativeFilesystemModuleBytecodeOpt1,
969 );
970 process_field(
971 &mut blob_sections,
972 resource,
973 ResourceField::RelativeFilesystemModuleBytecodeOpt2,
974 );
975 process_field(
976 &mut blob_sections,
977 resource,
978 ResourceField::RelativeFilesystemExtensionModuleSharedLibrary,
979 );
980 process_field(
981 &mut blob_sections,
982 resource,
983 ResourceField::RelativeFilesystemPackageResources,
984 );
985 process_field(
986 &mut blob_sections,
987 resource,
988 ResourceField::RelativeFilesystemDistributionResource,
989 );
990 process_field(
991 &mut blob_sections,
992 resource,
993 ResourceField::FileDataEmbedded,
994 );
995 process_field(
996 &mut blob_sections,
997 resource,
998 ResourceField::FileDataUtf8RelativePath,
999 );
1000 }
1001
1002 for section in blob_sections.values() {
1003 blob_section_count += 1;
1004 blob_index_length += section.index_v1_length();
1005 }
1006
1007 dest.write_all(HEADER_V3)?;
1008
1009 dest.write_u8(blob_section_count)?;
1010 dest.write_u32::<LittleEndian>(blob_index_length as u32)?;
1011 dest.write_u32::<LittleEndian>(resources.len() as u32)?;
1012 dest.write_u32::<LittleEndian>(resource_index_length as u32)?;
1013
1014 for section in blob_sections.values() {
1016 section.write_index_v1(dest)?;
1017 }
1018 dest.write_u8(ResourceField::EndOfIndex.into())?;
1019
1020 for resource in resources {
1022 resource.as_ref().write_index_v1(dest)?;
1023 }
1024 dest.write_u8(ResourceField::EndOfIndex.into())?;
1025
1026 for resource in resources {
1028 dest.write_all(resource.as_ref().name.as_bytes())?;
1029 add_interior_padding(dest)?;
1030 }
1031
1032 for resource in resources {
1033 if let Some(data) = &resource.as_ref().in_memory_source {
1034 dest.write_all(data)?;
1035 add_interior_padding(dest)?;
1036 }
1037 }
1038
1039 for resource in resources {
1040 if let Some(data) = &resource.as_ref().in_memory_bytecode {
1041 dest.write_all(data)?;
1042 add_interior_padding(dest)?;
1043 }
1044 }
1045
1046 for resource in resources {
1047 if let Some(data) = &resource.as_ref().in_memory_bytecode_opt1 {
1048 dest.write_all(data)?;
1049 add_interior_padding(dest)?;
1050 }
1051 }
1052
1053 for resource in resources {
1054 if let Some(data) = &resource.as_ref().in_memory_bytecode_opt2 {
1055 dest.write_all(data)?;
1056 add_interior_padding(dest)?;
1057 }
1058 }
1059
1060 for resource in resources {
1061 if let Some(data) = &resource.as_ref().in_memory_extension_module_shared_library {
1062 dest.write_all(data)?;
1063 add_interior_padding(dest)?;
1064 }
1065 }
1066
1067 for resource in resources {
1068 if let Some(resources) = &resource.as_ref().in_memory_package_resources {
1069 for (key, value) in resources.iter() {
1070 dest.write_all(key.as_bytes())?;
1071 add_interior_padding(dest)?;
1072 dest.write_all(value)?;
1073 add_interior_padding(dest)?;
1074 }
1075 }
1076 }
1077
1078 for resource in resources {
1079 if let Some(resources) = &resource.as_ref().in_memory_distribution_resources {
1080 for (key, value) in resources {
1081 dest.write_all(key.as_bytes())?;
1082 add_interior_padding(dest)?;
1083 dest.write_all(value)?;
1084 add_interior_padding(dest)?;
1085 }
1086 }
1087 }
1088
1089 for resource in resources {
1090 if let Some(data) = &resource.as_ref().in_memory_shared_library {
1091 dest.write_all(data)?;
1092 add_interior_padding(dest)?;
1093 }
1094 }
1095
1096 for resource in resources {
1097 if let Some(names) = &resource.as_ref().shared_library_dependency_names {
1098 for name in names {
1099 dest.write_all(name.as_bytes())?;
1100 add_interior_padding(dest)?;
1101 }
1102 }
1103 }
1104
1105 for resource in resources {
1106 if let Some(path) = &resource.as_ref().relative_path_module_source {
1107 dest.write_all(&path_to_bytes(path))?;
1108 add_interior_padding(dest)?;
1109 }
1110 }
1111
1112 for resource in resources {
1113 if let Some(path) = &resource.as_ref().relative_path_module_bytecode {
1114 dest.write_all(&path_to_bytes(path))?;
1115 add_interior_padding(dest)?;
1116 }
1117 }
1118
1119 for resource in resources {
1120 if let Some(path) = &resource.as_ref().relative_path_module_bytecode_opt1 {
1121 dest.write_all(&path_to_bytes(path))?;
1122 add_interior_padding(dest)?;
1123 }
1124 }
1125
1126 for resource in resources {
1127 if let Some(path) = &resource.as_ref().relative_path_module_bytecode_opt2 {
1128 dest.write_all(&path_to_bytes(path))?;
1129 add_interior_padding(dest)?;
1130 }
1131 }
1132
1133 for resource in resources {
1134 if let Some(path) = &resource
1135 .as_ref()
1136 .relative_path_extension_module_shared_library
1137 {
1138 dest.write_all(&path_to_bytes(path))?;
1139 add_interior_padding(dest)?;
1140 }
1141 }
1142
1143 for resource in resources {
1144 if let Some(resources) = &resource.as_ref().relative_path_package_resources {
1145 for (key, path) in resources.iter() {
1146 dest.write_all(key.as_bytes())?;
1147 add_interior_padding(dest)?;
1148 dest.write_all(&path_to_bytes(path))?;
1149 add_interior_padding(dest)?;
1150 }
1151 }
1152 }
1153
1154 for resource in resources {
1155 if let Some(resources) = &resource.as_ref().relative_path_distribution_resources {
1156 for (key, path) in resources {
1157 dest.write_all(key.as_bytes())?;
1158 add_interior_padding(dest)?;
1159 dest.write_all(&path_to_bytes(path))?;
1160 add_interior_padding(dest)?;
1161 }
1162 }
1163 }
1164
1165 for resource in resources {
1166 if let Some(data) = &resource.as_ref().file_data_embedded {
1167 dest.write_all(data)?;
1168 add_interior_padding(dest)?;
1169 }
1170 }
1171
1172 for resource in resources {
1173 if let Some(path) = &resource.as_ref().file_data_utf8_relative_path {
1174 dest.write_all(path.as_bytes())?;
1175 add_interior_padding(dest)?;
1176 }
1177 }
1178
1179 Ok(())
1180}
1181
1182#[cfg(test)]
1183mod tests {
1184 use {super::*, std::borrow::Cow};
1185
1186 #[test]
1187 fn test_write_empty() -> Result<()> {
1188 let mut data = Vec::new();
1189 let resources: Vec<Resource<u8>> = Vec::new();
1190 write_packed_resources_v3(&resources, &mut data, None)?;
1191
1192 let mut expected: Vec<u8> = b"pyembed\x03".to_vec();
1193 expected.write_u8(0)?;
1195 expected.write_u32::<LittleEndian>(1)?;
1197 expected.write_u32::<LittleEndian>(0)?;
1199 expected.write_u32::<LittleEndian>(1)?;
1201 expected.write_u8(0)?;
1203 expected.write_u8(0)?;
1204
1205 assert_eq!(data, expected);
1206
1207 Ok(())
1208 }
1209
1210 #[test]
1211 fn test_write_resource_name() -> Result<()> {
1212 let mut data = Vec::new();
1213 let resource = Resource {
1214 name: Cow::Owned("foo".to_string()),
1215 ..Resource::default()
1216 };
1217
1218 write_packed_resources_v3(&[resource], &mut data, None)?;
1219
1220 let mut expected: Vec<u8> = b"pyembed\x03".to_vec();
1221 expected.write_u8(1)?;
1223 expected.write_u32::<LittleEndian>(1 + 1 + 1 + 1 + 8 + 1 + 1)?;
1225 expected.write_u32::<LittleEndian>(1)?;
1227 expected.write_u32::<LittleEndian>(1 + 1 + 2 + 1 + 1)?;
1230 expected.write_u8(BlobSectionField::StartOfEntry.into())?;
1232 expected.write_u8(BlobSectionField::ResourceFieldType.into())?;
1233 expected.write_u8(ResourceField::Name.into())?;
1234 expected.write_u8(BlobSectionField::RawPayloadLength.into())?;
1235 expected.write_u64::<LittleEndian>(b"foo".len() as u64)?;
1236 expected.write_u8(BlobSectionField::EndOfEntry.into())?;
1237 expected.write_u8(BlobSectionField::EndOfIndex.into())?;
1238 expected.write_u8(ResourceField::StartOfEntry.into())?;
1240 expected.write_u8(ResourceField::Name.into())?;
1241 expected.write_u16::<LittleEndian>(b"foo".len() as u16)?;
1242 expected.write_u8(ResourceField::EndOfEntry.into())?;
1243 expected.write_u8(ResourceField::EndOfIndex.into())?;
1244 expected.write_all(b"foo")?;
1245
1246 assert_eq!(data, expected);
1247
1248 Ok(())
1249 }
1250}