]>
git.proxmox.com Git - rustc.git/blob - vendor/object-0.27.1/src/write/string.rs
1 use indexmap
::IndexSet
;
4 /// An identifer for an entry in a string table.
5 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
6 pub struct StringId(usize);
8 #[derive(Debug, Default)]
9 pub(crate) struct StringTable
<'a
> {
10 strings
: IndexSet
<&'a
[u8]>,
14 impl<'a
> StringTable
<'a
> {
15 /// Add a string to the string table.
17 /// Panics if the string table has already been written, or
18 /// if the string contains a null byte.
19 pub fn add(&mut self, string
: &'a
[u8]) -> StringId
{
20 assert
!(self.offsets
.is_empty());
21 assert
!(!string
.contains(&0));
22 let id
= self.strings
.insert_full(string
).0;
26 /// Return the id of the given string.
28 /// Panics if the string is not in the string table.
29 pub fn get_id(&self, string
: &[u8]) -> StringId
{
30 let id
= self.strings
.get_index_of(string
).unwrap();
34 /// Return the string for the given id.
36 /// Panics if the string is not in the string table.
37 pub fn get_string(&self, id
: StringId
) -> &'a
[u8] {
38 self.strings
.get_index(id
.0).unwrap()
41 /// Return the offset of the given string.
43 /// Panics if the string table has not been written, or
44 /// if the string is not in the string table.
45 pub fn get_offset(&self, id
: StringId
) -> usize {
49 /// Append the string table to the given `Vec`, and
50 /// calculate the list of string offsets.
52 /// `base` is the initial string table offset. For example,
53 /// this should be 1 for ELF, to account for the initial
54 /// null byte (which must have been written by the caller).
55 pub fn write(&mut self, base
: usize, w
: &mut Vec
<u8>) {
56 assert
!(self.offsets
.is_empty());
58 let mut ids
: Vec
<_
> = (0..self.strings
.len()).collect();
59 sort(&mut ids
, 1, &self.strings
);
61 self.offsets
= vec
![0; ids
.len()];
62 let mut offset
= base
;
63 let mut previous
= &[][..];
65 let string
= self.strings
.get_index(id
).unwrap();
66 if previous
.ends_with(string
) {
67 self.offsets
[id
] = offset
- string
.len() - 1;
69 self.offsets
[id
] = offset
;
70 w
.extend_from_slice(string
);
72 offset
+= string
.len() + 1;
79 // Multi-key quicksort.
81 // Ordering is such that if a string is a suffix of at least one other string,
82 // then it is placed immediately after one of those strings. That is:
83 // - comparison starts at the end of the string
84 // - shorter strings come later
86 // Based on the implementation in LLVM.
87 fn sort(mut ids
: &mut [usize], mut pos
: usize, strings
: &IndexSet
<&[u8]>) {
93 let pivot
= byte(ids
[0], pos
, strings
);
95 let mut upper
= ids
.len();
98 let b
= byte(ids
[i
], pos
, strings
);
103 } else if b
< pivot
{
111 sort(&mut ids
[..lower
], pos
, strings
);
112 sort(&mut ids
[upper
..], pos
, strings
);
117 ids
= &mut ids
[lower
..upper
];
122 fn byte(id
: usize, pos
: usize, strings
: &IndexSet
<&[u8]>) -> u8 {
123 let string
= strings
.get_index(id
).unwrap();
124 let len
= string
.len();
128 // We know the strings don't contain null bytes.
139 let mut table
= StringTable
::default();
140 let id0
= table
.add(b
"");
141 let id1
= table
.add(b
"foo");
142 let id2
= table
.add(b
"bar");
143 let id3
= table
.add(b
"foobar");
145 let mut data
= Vec
::new();
147 table
.write(1, &mut data
);
148 assert_eq
!(data
, b
"\0foobar\0foo\0");
150 assert_eq
!(table
.get_offset(id0
), 11);
151 assert_eq
!(table
.get_offset(id1
), 8);
152 assert_eq
!(table
.get_offset(id2
), 4);
153 assert_eq
!(table
.get_offset(id3
), 1);