1use rustc_hash::FxHashMap;
2use sway_ir::{
3 size_bytes_round_up_to_word_alignment, ConstantContent, ConstantValue, Context, Padding,
4};
5
6use std::fmt;
7
8#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
9pub enum EntryName {
10 NonConfigurable,
11 Configurable(String),
12}
13
14impl fmt::Display for EntryName {
15 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
16 match self {
17 EntryName::NonConfigurable => write!(f, "NonConfigurable"),
18 EntryName::Configurable(name) => write!(f, "<Configurable, {name}>"),
19 }
20 }
21}
22
23#[derive(Clone, Debug, serde::Serialize)]
26pub struct Entry {
27 pub value: Datum,
28 pub padding: Padding,
29 pub name: EntryName,
30}
31
32#[derive(Clone, Debug, serde::Serialize)]
33pub enum Datum {
34 Byte(u8),
35 Word(u64),
36 ByteArray(Vec<u8>),
37 Slice(Vec<u8>),
38 Collection(Vec<Entry>),
39}
40
41impl Entry {
42 pub(crate) fn new_byte(value: u8, name: EntryName, padding: Option<Padding>) -> Entry {
43 Entry {
44 value: Datum::Byte(value),
45 padding: padding.unwrap_or(Padding::default_for_u8(value)),
46 name,
47 }
48 }
49
50 pub(crate) fn new_word(value: u64, name: EntryName, padding: Option<Padding>) -> Entry {
51 Entry {
52 value: Datum::Word(value),
53 padding: padding.unwrap_or(Padding::default_for_u64(value)),
54 name,
55 }
56 }
57
58 pub(crate) fn new_byte_array(
59 bytes: Vec<u8>,
60 name: EntryName,
61 padding: Option<Padding>,
62 ) -> Entry {
63 Entry {
64 padding: padding.unwrap_or(Padding::default_for_byte_array(&bytes)),
65 value: Datum::ByteArray(bytes),
66 name,
67 }
68 }
69
70 pub(crate) fn new_slice(bytes: Vec<u8>, name: EntryName, padding: Option<Padding>) -> Entry {
71 Entry {
72 padding: padding.unwrap_or(Padding::default_for_byte_array(&bytes)),
73 value: Datum::Slice(bytes),
74 name,
75 }
76 }
77
78 pub(crate) fn new_collection(
79 elements: Vec<Entry>,
80 name: EntryName,
81 padding: Option<Padding>,
82 ) -> Entry {
83 Entry {
84 padding: padding.unwrap_or(Padding::default_for_aggregate(
85 elements.iter().map(|el| el.padding.target_size()).sum(),
86 )),
87 value: Datum::Collection(elements),
88 name,
89 }
90 }
91
92 pub(crate) fn from_constant(
93 context: &Context,
94 constant: &ConstantContent,
95 name: EntryName,
96 padding: Option<Padding>,
97 ) -> Entry {
98 if constant.ty.is_enum(context) {
100 let (tag, value) = constant
101 .enum_tag_and_value_with_paddings(context)
102 .expect("Constant is an enum.");
103
104 let tag_entry = Entry::from_constant(context, tag.0, EntryName::NonConfigurable, tag.1);
105 let value_entry =
106 Entry::from_constant(context, value.0, EntryName::NonConfigurable, value.1);
107
108 return Entry::new_collection(vec![tag_entry, value_entry], name, padding);
109 }
110
111 match &constant.value {
113 ConstantValue::Undef | ConstantValue::Unit => Entry::new_byte(0, name, padding),
114 ConstantValue::Bool(value) => Entry::new_byte(u8::from(*value), name, padding),
115 ConstantValue::Uint(value) => {
116 if constant.ty.is_uint8(context) {
117 Entry::new_byte(*value as u8, name, padding)
118 } else {
119 Entry::new_word(*value, name, padding)
120 }
121 }
122 ConstantValue::U256(value) => {
123 Entry::new_byte_array(value.to_be_bytes().to_vec(), name, padding)
124 }
125 ConstantValue::B256(value) => {
126 Entry::new_byte_array(value.to_be_bytes().to_vec(), name, padding)
127 }
128 ConstantValue::String(bytes) => Entry::new_byte_array(bytes.clone(), name, padding),
129 ConstantValue::Array(_) => Entry::new_collection(
130 constant
131 .array_elements_with_padding(context)
132 .expect("Constant is an array.")
133 .into_iter()
134 .map(|(elem, padding)| {
135 Entry::from_constant(context, elem, EntryName::NonConfigurable, padding)
136 })
137 .collect(),
138 name,
139 padding,
140 ),
141 ConstantValue::Struct(_) => Entry::new_collection(
142 constant
143 .struct_fields_with_padding(context)
144 .expect("Constant is a struct.")
145 .into_iter()
146 .map(|(elem, padding)| {
147 Entry::from_constant(context, elem, EntryName::NonConfigurable, padding)
148 })
149 .collect(),
150 name,
151 padding,
152 ),
153 ConstantValue::RawUntypedSlice(bytes) => Entry::new_slice(bytes.clone(), name, padding),
154 ConstantValue::Reference(_) => {
155 todo!("Constant references are currently not supported.")
156 }
157 ConstantValue::Slice(_) => {
158 todo!("Constant slices are currently not supported.")
159 }
160 }
161 }
162
163 pub(crate) fn to_bytes(&self) -> Vec<u8> {
165 let bytes = match &self.value {
167 Datum::Byte(value) => vec![*value],
168 Datum::Word(value) => value.to_be_bytes().to_vec(),
169 Datum::ByteArray(bytes) | Datum::Slice(bytes) if bytes.len() % 8 == 0 => bytes.clone(),
170 Datum::ByteArray(bytes) | Datum::Slice(bytes) => bytes
171 .iter()
172 .chain([0; 8].iter())
173 .copied()
174 .take((bytes.len() + 7) & 0xfffffff8_usize)
175 .collect(),
176 Datum::Collection(items) => items.iter().flat_map(|el| el.to_bytes()).collect(),
177 };
178
179 let final_padding = self.padding.target_size().saturating_sub(bytes.len());
180 match self.padding {
181 Padding::Left { .. } => {
182 [std::iter::repeat_n(0u8, final_padding).collect(), bytes].concat()
183 }
184 Padding::Right { .. } => {
185 [bytes, std::iter::repeat_n(0u8, final_padding).collect()].concat()
186 }
187 }
188 }
189
190 pub(crate) fn has_copy_type(&self) -> bool {
191 matches!(self.value, Datum::Word(_) | Datum::Byte(_))
192 }
193
194 pub(crate) fn is_byte(&self) -> bool {
195 matches!(self.value, Datum::Byte(_))
196 }
197
198 pub(crate) fn equiv(&self, entry: &Entry) -> bool {
199 fn equiv_data(lhs: &Datum, rhs: &Datum) -> bool {
200 match (lhs, rhs) {
201 (Datum::Byte(l), Datum::Byte(r)) => l == r,
202 (Datum::Word(l), Datum::Word(r)) => l == r,
203 (Datum::ByteArray(l), Datum::ByteArray(r)) => l == r,
204 (Datum::Collection(l), Datum::Collection(r)) => {
205 l.len() == r.len()
206 && l.iter()
207 .zip(r.iter())
208 .all(|(l, r)| equiv_data(&l.value, &r.value))
209 }
210 _ => false,
211 }
212 }
213
214 equiv_data(&self.value, &entry.value) && self.name == entry.name
219 }
220}
221
222#[derive(Clone, Debug)]
223pub enum DataIdEntryKind {
224 NonConfigurable,
225 Configurable,
226}
227
228impl fmt::Display for DataIdEntryKind {
229 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
230 match self {
231 DataIdEntryKind::NonConfigurable => write!(f, "NonConfigurable"),
232 DataIdEntryKind::Configurable => write!(f, "Configurable"),
233 }
234 }
235}
236
237#[derive(Clone, Debug)]
239pub(crate) struct DataId {
240 pub(crate) idx: u32,
241 pub(crate) kind: DataIdEntryKind,
242}
243
244impl fmt::Display for DataId {
245 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
246 write!(f, "data_{}_{}", self.kind, self.idx)
247 }
248}
249
250#[derive(Default, Clone, Debug)]
252pub struct DataSection {
253 pub non_configurables: Vec<Entry>,
254 pub configurables: Vec<Entry>,
255 pub(crate) pointer_id: FxHashMap<u64, DataId>,
256}
257
258impl DataSection {
259 pub fn num_entries(&self) -> usize {
261 self.non_configurables.len() + self.configurables.len()
262 }
263
264 pub fn iter_all_entries(&self) -> impl Iterator<Item = Entry> + '_ {
266 self.non_configurables
267 .iter()
268 .chain(self.configurables.iter())
269 .cloned()
270 }
271
272 fn absolute_idx(&self, id: &DataId) -> usize {
274 match id.kind {
275 DataIdEntryKind::NonConfigurable => id.idx as usize,
276 DataIdEntryKind::Configurable => id.idx as usize + self.non_configurables.len(),
277 }
278 }
279
280 fn get(&self, id: &DataId) -> Option<&Entry> {
282 match id.kind {
283 DataIdEntryKind::NonConfigurable => self.non_configurables.get(id.idx as usize),
284 DataIdEntryKind::Configurable => self.configurables.get(id.idx as usize),
285 }
286 }
287
288 pub(crate) fn data_id_to_offset(&self, id: &DataId) -> usize {
291 let idx = self.absolute_idx(id);
292 self.absolute_idx_to_offset(idx)
293 }
294
295 pub(crate) fn absolute_idx_to_offset(&self, idx: usize) -> usize {
298 self.iter_all_entries().take(idx).fold(0, |offset, entry| {
299 size_bytes_round_up_to_word_alignment!(offset + entry.to_bytes().len())
301 })
302 }
303
304 pub(crate) fn serialize_to_bytes(&self) -> Vec<u8> {
305 let mut buf = Vec::with_capacity(self.num_entries());
307 for entry in self.iter_all_entries() {
308 buf.append(&mut entry.to_bytes());
309
310 let aligned_len = size_bytes_round_up_to_word_alignment!(buf.len());
312 buf.extend(vec![0u8; aligned_len - buf.len()]);
313 }
314 buf
315 }
316
317 pub(crate) fn has_copy_type(&self, id: &DataId) -> Option<bool> {
319 self.get(id).map(|entry| entry.has_copy_type())
320 }
321
322 pub(crate) fn is_byte(&self, id: &DataId) -> Option<bool> {
324 self.get(id).map(|entry| entry.is_byte())
325 }
326
327 pub(crate) fn append_pointer(&mut self, pointer_value: u64) -> DataId {
334 let data_id = self.insert_data_value(Entry::new_word(
336 pointer_value,
337 EntryName::NonConfigurable,
338 None,
339 ));
340 self.pointer_id.insert(pointer_value, data_id.clone());
341 data_id
342 }
343
344 pub(crate) fn data_id_of_pointer(&self, pointer_value: u64) -> Option<DataId> {
347 self.pointer_id.get(&pointer_value).cloned()
348 }
349
350 pub(crate) fn insert_data_value(&mut self, new_entry: Entry) -> DataId {
354 let (value_pairs, kind) = match new_entry.name {
357 EntryName::NonConfigurable => (
358 &mut self.non_configurables,
359 DataIdEntryKind::NonConfigurable,
360 ),
361 EntryName::Configurable(_) => (&mut self.configurables, DataIdEntryKind::Configurable),
362 };
363 match value_pairs.iter().position(|entry| entry.equiv(&new_entry)) {
364 Some(num) => DataId {
365 idx: num as u32,
366 kind,
367 },
368 None => {
369 value_pairs.push(new_entry);
370 DataId {
372 idx: (value_pairs.len() - 1) as u32,
373 kind,
374 }
375 }
376 }
377 }
378
379 pub(crate) fn get_data_word(&self, data_id: &DataId) -> Option<u64> {
381 let value_pairs = match data_id.kind {
382 DataIdEntryKind::NonConfigurable => &self.non_configurables,
383 DataIdEntryKind::Configurable => &self.configurables,
384 };
385 value_pairs.get(data_id.idx as usize).and_then(|entry| {
386 if let Datum::Word(w) = entry.value {
387 Some(w)
388 } else {
389 None
390 }
391 })
392 }
393}
394
395impl fmt::Display for DataSection {
396 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
397 fn display_entry(datum: &Datum) -> String {
398 match datum {
399 Datum::Byte(w) => format!(".byte {w}"),
400 Datum::Word(w) => format!(".word {w}"),
401 Datum::ByteArray(bs) => display_bytes_for_data_section(bs, ".bytes"),
402 Datum::Slice(bs) => display_bytes_for_data_section(bs, ".slice"),
403 Datum::Collection(els) => format!(
404 ".collection {{ {} }}",
405 els.iter()
406 .map(|el| display_entry(&el.value))
407 .collect::<Vec<_>>()
408 .join(", ")
409 ),
410 }
411 }
412
413 use std::fmt::Write;
414 let mut data_buf = String::new();
415 for (ix, entry) in self.iter_all_entries().enumerate() {
416 writeln!(
417 data_buf,
418 "data_{}_{} {}",
419 entry.name,
420 ix,
421 display_entry(&entry.value)
422 )?;
423 }
424
425 write!(f, ".data:\n{data_buf}")
426 }
427}
428
429fn display_bytes_for_data_section(bs: &Vec<u8>, prefix: &str) -> String {
430 let mut hex_str = String::new();
431 let mut chr_str = String::new();
432 for b in bs {
433 hex_str.push_str(format!("{b:02x} ").as_str());
434 chr_str.push(if *b == b' ' || b.is_ascii_graphic() {
435 *b as char
436 } else {
437 '.'
438 });
439 }
440 format!("{prefix}[{}] {hex_str} {chr_str}", bs.len())
441}