///|
/// View over an ELF string table section.
///
/// ELF string tables are byte arrays containing NUL-terminated strings. Offsets
/// into the table are stored in other records, for example `st_name` in symbols
/// and `sh_name` in section headers.
pub(all) struct StringTable {
  /// Raw string table bytes.
  data : BytesView
} derive(Debug, Eq)

///|
/// Construct a string table view from raw section bytes.
pub fn StringTable::new(data : BytesView) -> StringTable {
  { data, }
}

///|
/// Construct an empty string table.
///
/// Accessing any offset in the empty table raises `BadOffset`.
pub fn StringTable::default() -> StringTable {
  { data: b""[:] }
}

///|
/// Return the raw bytes for the NUL-terminated string at `offset`.
///
/// The returned view excludes the terminating NUL byte. Raises `BadOffset` if
/// the offset is outside the table and `StringTableMissingNul` if no terminator
/// appears before the table ends.
pub fn StringTable::get_raw(
  self : StringTable,
  offset : Int,
) -> BytesView raise ParseError {
  if offset < 0 {
    raise BadOffset(0UL)
  }
  if self.data.is_empty() || offset >= self.data.length() {
    raise BadOffset(offset.to_uint64())
  }
  let mut idx = offset
  while idx < self.data.length() {
    if self.data[idx] == 0 {
      return self.data[offset:idx]
    }
    idx += 1
  }
  raise StringTableMissingNul(offset.to_uint64())
}

///|
/// Decode the NUL-terminated string at `offset` as UTF-8.
///
/// This is a convenience wrapper around `get_raw`; it raises `Utf8Error` for
/// non-UTF-8 byte sequences.
pub fn StringTable::get(
  self : StringTable,
  offset : Int,
) -> String raise ParseError {
  let raw = self.get_raw(offset)
  @utf8.decode(raw) catch {
    _ => raise Utf8Error
  }
}