write: make StringTable public

Needed for use with elf::Encoder.

Also change StringId from usize to u32, since offsets are limited
to that anyway.
This commit is contained in:
Philip Craig
2026-05-29 21:28:48 +10:00
parent ceb1d59f82
commit cdd9d43a82
3 changed files with 24 additions and 30 deletions
+4 -4
View File
@@ -790,7 +790,7 @@ impl<'data> Builder<'data> {
if !self.sections.is_empty() {
section_num = 1;
}
let mut shstrtab = write::string::StringTable::new();
let mut shstrtab = write::StringTable::new();
for section in &self.sections {
let index = section_num;
out_sections_index[section.id.0] = index;
@@ -887,7 +887,7 @@ impl<'data> Builder<'data> {
}
// Add dynamic strings to string table.
let mut dynstr = write::string::StringTable::new();
let mut dynstr = write::StringTable::new();
for section in &self.sections {
if let SectionData::Dynamic(dynamics) = &section.data {
for dynamic in dynamics {
@@ -982,7 +982,7 @@ impl<'data> Builder<'data> {
// Assign symbol indices and add names to string table.
let mut out_syms = Vec::with_capacity(self.symbols.len());
let mut strtab = write::string::StringTable::new();
let mut strtab = write::StringTable::new();
let mut need_symtab_shndx = symtab_shndx_index != 0;
// Local symbols must come before global.
let local_symbols = self
@@ -1992,7 +1992,7 @@ impl<'data> Builder<'data> {
/// To get an accurate result, you may need to first call
/// [`Self::delete_orphan_symbols`] and [`Self::delete_unused_versions`].
pub fn dynamic_string_size(&self) -> usize {
let mut dynstr = write::string::StringTable::default();
let mut dynstr = write::StringTable::default();
for section in &self.sections {
if let SectionData::Dynamic(dynamics) = &section.data {
for dynamic in dynamics {
+2 -2
View File
@@ -42,8 +42,8 @@ pub mod pe;
#[cfg(feature = "xcoff")]
mod xcoff;
pub(crate) mod string;
pub use string::StringId;
mod string;
pub use string::*;
mod util;
pub use util::*;
+18 -24
View File
@@ -9,11 +9,11 @@ type IndexSet<K> = indexmap::IndexSet<K, hashbrown::DefaultHashBuilder>;
/// An identifier for an entry in a string table.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct StringId(usize);
pub struct StringId(u32);
/// A string table containing null terminated byte strings.
#[derive(Debug, Default)]
pub(crate) struct StringTable<'a> {
pub struct StringTable<'a> {
strings: IndexSet<&'a [u8]>,
offsets: Vec<u32>,
size: u64,
@@ -23,18 +23,16 @@ pub(crate) struct StringTable<'a> {
}
impl<'a> StringTable<'a> {
/// Construct a new string table.
#[allow(dead_code)]
pub(crate) fn new() -> Self {
/// Construct an empty string table.
pub fn new() -> Self {
StringTable::default()
}
/// Construct a new string table that writes the strings in
/// Construct an empty string table that writes the strings in
/// the order they are added.
///
/// This does not perform suffix merging.
#[allow(dead_code)]
pub(crate) fn new_in_order(base: u32) -> Self {
pub fn new_in_order(base: u32) -> Self {
StringTable {
strings: IndexSet::default(),
offsets: Vec::new(),
@@ -45,8 +43,7 @@ impl<'a> StringTable<'a> {
}
/// Return true if the string table contains no strings.
#[allow(dead_code)]
pub(crate) fn is_empty(&self) -> bool {
pub fn is_empty(&self) -> bool {
self.strings.is_empty()
}
@@ -58,7 +55,7 @@ impl<'a> StringTable<'a> {
///
/// The string must not contain a null byte; this is asserted here for
/// debug builds, and checked by `write` for all builds.
pub(crate) fn add(&mut self, string: &'a [u8]) -> StringId {
pub fn add(&mut self, string: &'a [u8]) -> StringId {
debug_assert!(self.in_order || self.offsets.is_empty());
debug_assert!(!string.contains(&0));
let (id, new) = self.strings.insert_full(string);
@@ -66,24 +63,22 @@ impl<'a> StringTable<'a> {
self.offsets.push(self.size as u32);
self.size += string.len() as u64 + 1;
}
StringId(id)
StringId(id as u32)
}
/// Return the id of a previously added string.
///
/// Panics if the string is not in the string table.
#[allow(dead_code)]
pub(crate) fn get_id(&self, string: &[u8]) -> StringId {
pub fn get_id(&self, string: &[u8]) -> StringId {
let id = self.strings.get_index_of(string).unwrap();
StringId(id)
StringId(id as u32)
}
/// Return the string for the given id.
///
/// Panics if `id` is invalid.
#[allow(dead_code)]
pub(crate) fn get_string(&self, id: StringId) -> &'a [u8] {
self.strings.get_index(id.0).unwrap()
pub fn get_string(&self, id: StringId) -> &'a [u8] {
self.strings.get_index(id.0 as usize).unwrap()
}
/// Return the offset of the given string.
@@ -95,15 +90,14 @@ impl<'a> StringTable<'a> {
/// overflows `u32`. Reporting the overflow is postponed to `write`.
///
/// Panics if `id` is invalid or `write` was not called.
pub(crate) fn get_offset(&self, id: StringId) -> u32 {
self.offsets[id.0]
pub fn get_offset(&self, id: StringId) -> u32 {
self.offsets[id.0 as usize]
}
/// Return the offset of an optional string.
///
/// Returns 0 if `id` is `None`. Otherwise see [`Self::get_offset`].
#[allow(dead_code)]
pub(crate) fn maybe_get_offset(&self, id: Option<StringId>) -> u32 {
pub fn maybe_get_offset(&self, id: Option<StringId>) -> u32 {
let Some(id) = id else {
return 0;
};
@@ -123,7 +117,7 @@ impl<'a> StringTable<'a> {
/// - `write` has already been called
/// - any string contains a null byte
/// - the string table size is > `u32::MAX`
pub(crate) fn write(&mut self, base: u32, w: &mut Vec<u8>) -> Result<u32> {
pub fn write(&mut self, base: u32, w: &mut Vec<u8>) -> Result<u32> {
if !self.in_order && !self.offsets.is_empty() {
return Err(Error("string table already written".into()));
}
@@ -168,7 +162,7 @@ impl<'a> StringTable<'a> {
/// `base` is the initial string table offset. For example,
/// this should be 1 for ELF, to account for the initial
/// null byte.
#[allow(dead_code)]
#[cfg(all(feature = "build_core", feature = "elf"))]
pub(crate) fn size(&self, base: usize) -> usize {
if self.in_order {
debug_assert_eq!(self.base as usize, base);