Skip to main content

gsym/writer/
mod.rs

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/// Settings controlling deterministic GSYM encoding.
16///
17/// Version, byte order, base address, and build ID.
18/// [`BuilderOptions`](crate::BuilderOptions) holds these alongside the
19/// model-level policy, and the [`GsymBuilder`] setters change the same values.
20///
21/// A `None` base address selects the lowest function address. `build_id` is
22/// opaque and is stored as given; version 1 rejects more than 20 bytes with
23/// [`Error::V1BuildIdTooLong`](crate::Error::V1BuildIdTooLong).
24#[derive(Clone, Eq, PartialEq)]
25pub struct WriterOptions {
26    /// Output format version.
27    pub version: GsymVersion,
28    /// Output byte order.
29    pub endian: Endian,
30    /// Explicit image base, or `None` to use the first function address.
31    pub base_address: Option<u64>,
32    /// Opaque build identifier copied into the output header.
33    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}