1use rustc_hash::FxHashMap;
2use sway_ir::{
3 size_bytes_round_up_to_word_alignment, ConstantContent, ConstantValue, Context, Padding,
4};
5
6use std::{fmt, iter::repeat};
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 { .. } => [repeat(0u8).take(final_padding).collect(), bytes].concat(),
182 Padding::Right { .. } => [bytes, repeat(0u8).take(final_padding).collect()].concat(),
183 }
184 }
185
186 pub(crate) fn has_copy_type(&self) -> bool {
187 matches!(self.value, Datum::Word(_) | Datum::Byte(_))
188 }
189
190 pub(crate) fn is_byte(&self) -> bool {
191 matches!(self.value, Datum::Byte(_))
192 }
193
194 pub(crate) fn equiv(&self, entry: &Entry) -> bool {
195 fn equiv_data(lhs: &Datum, rhs: &Datum) -> bool {
196 match (lhs, rhs) {
197 (Datum::Byte(l), Datum::Byte(r)) => l == r,
198 (Datum::Word(l), Datum::Word(r)) => l == r,
199 (Datum::ByteArray(l), Datum::ByteArray(r)) => l == r,
200 (Datum::Collection(l), Datum::Collection(r)) => {
201 l.len() == r.len()
202 && l.iter()
203 .zip(r.iter())
204 .all(|(l, r)| equiv_data(&l.value, &r.value))
205 }
206 _ => false,
207 }
208 }
209
210 equiv_data(&self.value, &entry.value) && self.name == entry.name
215 }
216}
217
218#[derive(Clone, Debug)]
219pub enum DataIdEntryKind {
220 NonConfigurable,
221 Configurable,
222}
223
224impl fmt::Display for DataIdEntryKind {
225 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226 match self {
227 DataIdEntryKind::NonConfigurable => write!(f, "NonConfigurable"),
228 DataIdEntryKind::Configurable => write!(f, "Configurable"),
229 }
230 }
231}
232
233#[derive(Clone, Debug)]
235pub(crate) struct DataId {
236 pub(crate) idx: u32,
237 pub(crate) kind: DataIdEntryKind,
238}
239
240impl fmt::Display for DataId {
241 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
242 write!(f, "data_{}_{}", self.kind, self.idx)
243 }
244}
245
246#[derive(Default, Clone, Debug)]
248pub struct DataSection {
249 pub non_configurables: Vec<Entry>,
250 pub configurables: Vec<Entry>,
251 pub(crate) pointer_id: FxHashMap<u64, DataId>,
252}
253
254impl DataSection {
255 pub fn num_entries(&self) -> usize {
257 self.non_configurables.len() + self.configurables.len()
258 }
259
260 pub fn iter_all_entries(&self) -> impl Iterator<Item = Entry> + '_ {
262 self.non_configurables
263 .iter()
264 .chain(self.configurables.iter())
265 .cloned()
266 }
267
268 fn absolute_idx(&self, id: &DataId) -> usize {
270 match id.kind {
271 DataIdEntryKind::NonConfigurable => id.idx as usize,
272 DataIdEntryKind::Configurable => id.idx as usize + self.non_configurables.len(),
273 }
274 }
275
276 fn get(&self, id: &DataId) -> Option<&Entry> {
278 match id.kind {
279 DataIdEntryKind::NonConfigurable => self.non_configurables.get(id.idx as usize),
280 DataIdEntryKind::Configurable => self.configurables.get(id.idx as usize),
281 }
282 }
283
284 pub(crate) fn data_id_to_offset(&self, id: &DataId) -> usize {
287 let idx = self.absolute_idx(id);
288 self.absolute_idx_to_offset(idx)
289 }
290
291 pub(crate) fn absolute_idx_to_offset(&self, idx: usize) -> usize {
294 self.iter_all_entries().take(idx).fold(0, |offset, entry| {
295 size_bytes_round_up_to_word_alignment!(offset + entry.to_bytes().len())
297 })
298 }
299
300 pub(crate) fn serialize_to_bytes(&self) -> Vec<u8> {
301 let mut buf = Vec::with_capacity(self.num_entries());
303 for entry in self.iter_all_entries() {
304 buf.append(&mut entry.to_bytes());
305
306 let aligned_len = size_bytes_round_up_to_word_alignment!(buf.len());
308 buf.extend(vec![0u8; aligned_len - buf.len()]);
309 }
310 buf
311 }
312
313 pub(crate) fn has_copy_type(&self, id: &DataId) -> Option<bool> {
315 self.get(id).map(|entry| entry.has_copy_type())
316 }
317
318 pub(crate) fn is_byte(&self, id: &DataId) -> Option<bool> {
320 self.get(id).map(|entry| entry.is_byte())
321 }
322
323 pub(crate) fn append_pointer(&mut self, pointer_value: u64) -> DataId {
330 let data_id = self.insert_data_value(Entry::new_word(
332 pointer_value,
333 EntryName::NonConfigurable,
334 None,
335 ));
336 self.pointer_id.insert(pointer_value, data_id.clone());
337 data_id
338 }
339
340 pub(crate) fn data_id_of_pointer(&self, pointer_value: u64) -> Option<DataId> {
343 self.pointer_id.get(&pointer_value).cloned()
344 }
345
346 pub(crate) fn insert_data_value(&mut self, new_entry: Entry) -> DataId {
350 let (value_pairs, kind) = match new_entry.name {
353 EntryName::NonConfigurable => (
354 &mut self.non_configurables,
355 DataIdEntryKind::NonConfigurable,
356 ),
357 EntryName::Configurable(_) => (&mut self.configurables, DataIdEntryKind::Configurable),
358 };
359 match value_pairs.iter().position(|entry| entry.equiv(&new_entry)) {
360 Some(num) => DataId {
361 idx: num as u32,
362 kind,
363 },
364 None => {
365 value_pairs.push(new_entry);
366 DataId {
368 idx: (value_pairs.len() - 1) as u32,
369 kind,
370 }
371 }
372 }
373 }
374
375 pub(crate) fn get_data_word(&self, data_id: &DataId) -> Option<u64> {
377 let value_pairs = match data_id.kind {
378 DataIdEntryKind::NonConfigurable => &self.non_configurables,
379 DataIdEntryKind::Configurable => &self.configurables,
380 };
381 value_pairs.get(data_id.idx as usize).and_then(|entry| {
382 if let Datum::Word(w) = entry.value {
383 Some(w)
384 } else {
385 None
386 }
387 })
388 }
389}
390
391impl fmt::Display for DataSection {
392 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
393 fn display_entry(datum: &Datum) -> String {
394 match datum {
395 Datum::Byte(w) => format!(".byte {w}"),
396 Datum::Word(w) => format!(".word {w}"),
397 Datum::ByteArray(bs) => display_bytes_for_data_section(bs, ".bytes"),
398 Datum::Slice(bs) => display_bytes_for_data_section(bs, ".slice"),
399 Datum::Collection(els) => format!(
400 ".collection {{ {} }}",
401 els.iter()
402 .map(|el| display_entry(&el.value))
403 .collect::<Vec<_>>()
404 .join(", ")
405 ),
406 }
407 }
408
409 use std::fmt::Write;
410 let mut data_buf = String::new();
411 for (ix, entry) in self.iter_all_entries().enumerate() {
412 writeln!(
413 data_buf,
414 "data_{}_{} {}",
415 entry.name,
416 ix,
417 display_entry(&entry.value)
418 )?;
419 }
420
421 write!(f, ".data:\n{data_buf}")
422 }
423}
424
425fn display_bytes_for_data_section(bs: &Vec<u8>, prefix: &str) -> String {
426 let mut hex_str = String::new();
427 let mut chr_str = String::new();
428 for b in bs {
429 hex_str.push_str(format!("{b:02x} ").as_str());
430 chr_str.push(if *b == b' ' || b.is_ascii_graphic() {
431 *b as char
432 } else {
433 '.'
434 });
435 }
436 format!("{prefix}[{}] {hex_str} {chr_str}", bs.len())
437}