1mod encode;
2mod finalize;
3mod semantic;
4
5use std::fmt;
6use std::io::Write;
7
8use self::encode::{address_offset_width, encode_v1, encode_v2};
9use self::finalize::finalize;
10use self::semantic::{StringTable, encode_function, intern_files};
11use crate::builder::GsymBuilder;
12use crate::validation::{validate_file_table, validate_for_writer};
13use crate::{Endian, Error, GsymVersion, Result};
14
15#[derive(Clone, Eq, PartialEq)]
25pub struct WriterOptions {
26 pub version: GsymVersion,
28 pub endian: Endian,
30 pub base_address: Option<u64>,
32 pub build_id: Vec<u8>,
34}
35
36impl fmt::Debug for WriterOptions {
37 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
38 formatter
39 .debug_struct("WriterOptions")
40 .field("version", &self.version)
41 .field("endian", &self.endian)
42 .field("base_address", &self.base_address)
43 .field("build_id_len", &self.build_id.len())
44 .finish()
45 }
46}
47
48impl Default for WriterOptions {
49 fn default() -> Self {
50 Self {
51 version: GsymVersion::V1,
52 endian: Endian::Little,
53 base_address: None,
54 build_id: Vec::new(),
55 }
56 }
57}
58
59pub(crate) fn write_builder(builder: GsymBuilder, mut output: impl Write) -> Result<()> {
60 let image = encode_builder(builder)?;
61 output.write_all(&image.prefix)?;
62 output.write_all(&image.function_info)?;
63 Ok(())
64}
65
66fn encode_builder(builder: GsymBuilder) -> Result<encode::EncodedImage> {
67 let (options, files, functions) = builder.into_parts();
68 validate_file_table(&files)?;
69 for function in &functions {
70 validate_for_writer(function, files.len())?;
71 }
72 let functions = finalize(functions, &options);
73 let first = functions
74 .first()
75 .ok_or(Error::InvalidModel("at least one function is required"))?;
76 let base = options.writer.base_address.unwrap_or(first.range.start);
77 if functions.iter().any(|function| function.range.start < base) {
78 return Err(Error::InvalidModel(
79 "function address precedes the selected image base",
80 ));
81 }
82 let width = address_offset_width(
83 functions
84 .last()
85 .ok_or(Error::InvalidModel("at least one function is required"))?
86 .range
87 .start
88 .saturating_sub(base),
89 );
90
91 let mut strings = StringTable::with_capacity(
92 functions
93 .len()
94 .saturating_add(files.len().saturating_mul(2)),
95 );
96 let encoded_files = intern_files(&files, &mut strings);
97 let mut encoded_functions = Vec::with_capacity(functions.len());
98 for function in functions {
99 encoded_functions.push(encode_function(function, &mut strings)?);
100 }
101 let image = match options.writer.version {
102 GsymVersion::V1 => encode_v1(
103 &options.writer,
104 base,
105 width,
106 &encoded_files,
107 strings.bytes(),
108 &encoded_functions,
109 )?,
110 GsymVersion::V2 => encode_v2(
111 &options.writer,
112 base,
113 width,
114 &encoded_files,
115 strings.bytes(),
116 &encoded_functions,
117 )?,
118 };
119 Ok(image)
120}
121
122pub(crate) fn encode_builder_to_bytes(builder: GsymBuilder) -> Result<Vec<u8>> {
123 encode_builder(builder).map(encode::EncodedImage::into_bytes)
124}