bpf_script/
types.rs

1use crate::error::{Error, Result};
2
3use std::collections::HashMap;
4
5/// Represents the physical properties of an integer.
6#[derive(Clone, Copy, Debug, Default)]
7pub struct Integer {
8    /// The total number of bits used to store the integer.
9    pub used_bits: u32,
10
11    /// The number of bits used when performing operations, less than `used_bits`.
12    pub bits: u32,
13
14    /// Whether the integer is signed.
15    pub is_signed: bool,
16}
17
18impl Integer {
19    /// Returns the size of the integer in bytes.
20    pub fn get_size(&self) -> u32 {
21        self.used_bits / 8
22    }
23}
24
25/// Represents the physical properties of a float.
26#[derive(Clone, Copy, Debug, Default)]
27pub struct Float {
28    /// The number of bits used to store and perform operations on.
29    pub bits: u32,
30}
31
32impl Float {
33    /// Returns the size of the float in bytes.
34    pub fn get_size(&self) -> u32 {
35        self.bits / 8
36    }
37}
38
39/// Represents the physical properties of an array.
40#[derive(Clone, Copy, Debug, Default)]
41pub struct Array {
42    /// The type of element the array stores.
43    pub element_type_id: usize,
44
45    /// The number of elements in the array.
46    pub num_elements: u32,
47
48    /// Cached size.
49    pub size: u32,
50}
51
52impl Array {
53    /// Creates a new array referencing the given database.
54    ///
55    /// # Arguments
56    ///
57    /// * `database` - The database that contains the element type id.
58    /// * `element_type_id` - The type id of the array elements.
59    /// * `num_elements` - The number of elements in the array.
60    pub fn create(
61        database: &TypeDatabase,
62        element_type_id: usize,
63        num_elements: u32,
64    ) -> Result<Self> {
65        let element_type = database
66            .get_type_by_id(element_type_id)
67            .ok_or(Error::InvalidTypeId)?;
68        let size = element_type.get_size() * num_elements;
69        Ok(Self {
70            element_type_id,
71            num_elements,
72            size,
73        })
74    }
75
76    /// Returns the size of the array in bytes.
77    pub fn get_size(&self) -> u32 {
78        self.size
79    }
80}
81
82/// Represents the phystical properties of a field in a struct or union.
83#[derive(Clone, Copy, Debug, Default)]
84pub struct Field {
85    /// The offset, in bits, of the field.
86    pub offset: u32,
87
88    /// The type of the field.
89    pub type_id: usize,
90}
91
92impl Field {
93    /// Returns the field's type.
94    ///
95    /// # Arguments
96    ///
97    /// * `database` - The database containing this structure/field.
98    pub fn get_type<'a>(&self, database: &'a TypeDatabase) -> Option<&'a Type> {
99        database.get_type_by_id(self.type_id)
100    }
101}
102
103/// Represents the physical properties of a structure.
104#[derive(Clone, Debug, Default)]
105pub struct Struct {
106    /// A map of field name to field type.
107    pub fields: HashMap<String, Field>,
108
109    /// Cached size
110    pub size: u32,
111}
112
113impl Struct {
114    /// Create a new structure referencing the given database.
115    ///
116    /// # Arguments
117    ///
118    /// * `database` - The database in which the fields are contained.
119    /// * `fields` - The fields for the structure.
120    pub fn create(database: &TypeDatabase, fields: &[(&str, Field)]) -> Result<Self> {
121        let mut new_fields = HashMap::with_capacity(fields.len());
122        let mut bits = 0;
123        for (name, field) in fields {
124            let field_type = database
125                .get_type_by_id(field.type_id)
126                .ok_or(Error::InvalidTypeId)?;
127            let reach = field.offset + field_type.get_size() * 8;
128            if reach > bits {
129                bits = reach
130            }
131            new_fields.insert(name.to_string(), *field);
132        }
133
134        Ok(Self {
135            fields: new_fields,
136            size: bits / 8,
137        })
138    }
139
140    /// Returns the size of the structure in bytes.
141    pub fn get_size(&self) -> u32 {
142        self.size
143    }
144}
145
146/// Represents the physical properties of an enum type.
147#[derive(Clone, Debug, Default)]
148pub struct Enum {
149    /// The number of bits representing each value.
150    pub bits: u32,
151
152    /// An array holding a type of (name, value) for each enum value.
153    pub values: Vec<(String, i64)>,
154}
155
156impl Enum {
157    /// Returns the size of the enum values in bytes.
158    pub fn get_size(&self) -> u32 {
159        self.bits / 8
160    }
161}
162
163/// Represents the physical properties of a function.
164#[derive(Clone, Debug, Default)]
165pub struct Function {
166    /// The function parameters as an array of types.
167    pub param_type_ids: Vec<usize>,
168}
169
170impl Function {
171    /// Creates a function with the given type ids as parameters.
172    ///
173    /// # Arguments
174    ///
175    /// * `param_type_ids` - The type ids of the parameters.
176    pub fn create(param_type_ids: &[usize]) -> Self {
177        Self {
178            param_type_ids: param_type_ids.to_vec(),
179        }
180    }
181}
182
183/// Variant for holding any kind of type.
184#[derive(Clone, Debug, Default)]
185pub enum BaseType {
186    #[default]
187    Void,
188    Integer(Integer),
189    Float(Float),
190    Array(Array),
191    Struct(Struct),
192    Enum(Enum),
193    Function(Function),
194}
195
196impl BaseType {
197    /// Returns the number of bytes that the underlying type occupies. Types like
198    /// void, functions, and, in the future, opaque types return 0.
199    pub fn get_size(&self) -> u32 {
200        match self {
201            BaseType::Void => 0,
202            BaseType::Integer(t) => t.get_size(),
203            BaseType::Float(t) => t.get_size(),
204            BaseType::Array(t) => t.get_size(),
205            BaseType::Struct(t) => t.get_size(),
206            BaseType::Enum(t) => t.get_size(),
207            BaseType::Function(_) => 0,
208        }
209    }
210}
211
212/// Represents a fully-qualified type.
213#[derive(Clone, Debug, Default)]
214pub struct Type {
215    /// The concrete base type.
216    pub base_type: BaseType,
217
218    /// Number of references.
219    pub num_refs: u32,
220}
221
222impl Type {
223    /// Convenience function for determining whether this is a pointer
224    /// type or not (num_refs > 0). Makes code more clear.
225    pub fn is_pointer(&self) -> bool {
226        self.num_refs > 0
227    }
228
229    /// Gets the size, in bytes, of the underlying type. Returns 8, if
230    /// the type is a pointer; BPF uses a 64-bit instruction set, on 32-bit
231    /// systems, addresses will have the MSBs truncated.
232    pub fn get_size(&self) -> u32 {
233        if self.num_refs > 0 {
234            return 8;
235        }
236
237        self.base_type.get_size()
238    }
239}
240
241impl From<BaseType> for Type {
242    fn from(base_type: BaseType) -> Self {
243        Self {
244            base_type,
245            num_refs: 0,
246        }
247    }
248}
249
250/// Holds type information.
251#[derive(Clone, Debug, Default)]
252pub struct TypeDatabase {
253    /// Map of name to type.
254    types: Vec<Type>,
255    name_map: HashMap<String, usize>,
256}
257
258impl TypeDatabase {
259    /// Adds a type to the type database.
260    ///
261    /// # Arguments
262    ///
263    /// * `name` - The name of the type.
264    /// * `ty` - The type to add.
265    pub fn add_type(&mut self, name: Option<&str>, ty: &Type) -> Result<usize> {
266        if let Some(name) = name {
267            if let Some(index) = self.name_map.get(name) {
268                self.types[*index] = ty.clone();
269                Ok(*index)
270            } else {
271                let index = self.types.len();
272                self.types.push(ty.clone());
273                self.name_map.insert(name.to_string(), index);
274                Ok(index)
275            }
276        } else {
277            self.types.push(ty.clone());
278            Ok(self.types.len() - 1)
279        }
280    }
281
282    /// Finds a type in the database by name.
283    ///
284    /// # Arguments
285    ///
286    /// * `name` - The name of the type.
287    pub fn get_type_by_name(&self, name: &str) -> Option<&Type> {
288        let index = self.name_map.get(name)?;
289        self.types.get(*index)
290    }
291
292    /// Finds a type in the database by id.
293    ///
294    /// # Arguments
295    ///
296    /// * `id` - The id of the type.
297    pub fn get_type_by_id(&self, id: usize) -> Option<&Type> {
298        self.types.get(id)
299    }
300
301    /// Gets a type's id by its name.
302    ///
303    /// # Arguments
304    ///
305    /// * `name` - The name of the type.
306    pub fn get_type_id_by_name(&self, name: &str) -> Option<usize> {
307        Some(*self.name_map.get(name)?)
308    }
309
310    /// Convenience function for adding an integer type to the database.
311    ///
312    /// # Arguments
313    ///
314    /// * `name` - The name of the type.
315    /// * `bytes` - The number of bits
316    /// * `is_signed` - If the integer is signed.
317    pub fn add_integer(
318        &mut self,
319        name: Option<&str>,
320        bytes: u32,
321        is_signed: bool,
322    ) -> Result<usize> {
323        let bits = bytes * 8;
324        let new_integer = Integer {
325            used_bits: bits,
326            bits,
327            is_signed,
328        };
329
330        self.add_type(name, &BaseType::Integer(new_integer).into())
331    }
332
333    /// Convenience function for adding a float type to the database.
334    ///
335    /// # Arguments
336    ///
337    /// * `name` - The name of the type.
338    /// * `bits` - The number of bits.
339    pub fn add_float(&mut self, name: Option<&str>, bits: u32) -> Result<usize> {
340        let new_float = Float { bits };
341
342        self.add_type(name, &BaseType::Float(new_float).into())
343    }
344
345    /// Convenience function for adding an array to the database.
346    ///
347    /// # Arguments
348    ///
349    /// * `name` - The name of the type.
350    /// * `element_type_id` - The type id of the element.
351    /// * `num_elements` - The number of elements in the array.
352    pub fn add_array(
353        &mut self,
354        name: Option<&str>,
355        element_type_id: usize,
356        num_elements: u32,
357    ) -> Result<usize> {
358        let new_array = Array::create(self, element_type_id, num_elements)?;
359        self.add_type(name, &BaseType::Array(new_array).into())
360    }
361
362    /// Convenience function for adding a struct to the database.
363    ///
364    /// # Arguments
365    ///
366    /// * `name` - The name of the type.
367    /// * `fields` - The fields to add.
368    pub fn add_struct(&mut self, name: Option<&str>, fields: &[(&str, Field)]) -> Result<usize> {
369        let new_struct = Struct::create(self, fields)?;
370        self.add_type(name, &BaseType::Struct(new_struct).into())
371    }
372
373    /// Convenience function for adding a struct to the database using
374    /// a slice of (field_name, type_id). Types are added in order, and
375    /// packed together contiguously.
376    ///
377    /// # Arguments
378    ///
379    /// * `name` - The name of the type.
380    /// * `fields` - The fields to add (by id).
381    pub fn add_struct_by_ids(
382        &mut self,
383        name: Option<&str>,
384        fields: &[(&str, usize)],
385    ) -> Result<usize> {
386        let mut new_fields = Vec::with_capacity(fields.len());
387        let mut offset = 0;
388        for (field_name, type_id) in fields {
389            let field_type = self
390                .get_type_by_id(*type_id)
391                .ok_or(Error::InvalidTypeName)?;
392            let field = Field {
393                offset,
394                type_id: *type_id,
395            };
396            offset += field_type.get_size() * 8;
397            new_fields.push((*field_name, field));
398        }
399        let new_struct = Struct::create(self, new_fields.as_slice())?;
400        self.add_type(name, &BaseType::Struct(new_struct).into())
401    }
402
403    /// Convenience function for adding a struct to the database using
404    /// a slice of (field_name, type_name). Types are added in order, and
405    /// packed together contiguously.
406    ///
407    /// # Arguments
408    ///
409    /// * `name` - The name of the type.
410    /// * `fields` - The fields to add (by name).
411    pub fn add_struct_by_names(
412        &mut self,
413        name: Option<&str>,
414        fields: &[(&str, &str)],
415    ) -> Result<usize> {
416        let mut new_fields = Vec::with_capacity(fields.len());
417        let mut offset = 0;
418        for (field_name, type_name) in fields {
419            let field_type = self
420                .get_type_by_name(type_name)
421                .ok_or(Error::InvalidTypeName)?;
422            let type_id = self
423                .get_type_id_by_name(type_name)
424                .ok_or(Error::InvalidTypeName)?;
425            let field = Field { offset, type_id };
426            offset += field_type.get_size() * 8;
427            new_fields.push((*field_name, field));
428        }
429        let new_struct = Struct::create(self, new_fields.as_slice())?;
430        self.add_type(name, &BaseType::Struct(new_struct).into())
431    }
432}
433
434pub trait AddToTypeDatabase {
435    fn add_to_database(database: &mut TypeDatabase) -> Result<usize>;
436}
437
438impl AddToTypeDatabase for u8 {
439    fn add_to_database(database: &mut TypeDatabase) -> Result<usize> {
440        database.add_integer(Some("u8"), 1, false)
441    }
442}
443
444impl AddToTypeDatabase for u16 {
445    fn add_to_database(database: &mut TypeDatabase) -> Result<usize> {
446        database.add_integer(Some("u16"), 2, false)
447    }
448}
449
450impl AddToTypeDatabase for u32 {
451    fn add_to_database(database: &mut TypeDatabase) -> Result<usize> {
452        database.add_integer(Some("u32"), 4, false)
453    }
454}
455
456impl AddToTypeDatabase for u64 {
457    fn add_to_database(database: &mut TypeDatabase) -> Result<usize> {
458        database.add_integer(Some("u64"), 8, false)
459    }
460}
461
462impl AddToTypeDatabase for i8 {
463    fn add_to_database(database: &mut TypeDatabase) -> Result<usize> {
464        database.add_integer(Some("i8"), 1, true)
465    }
466}
467
468impl AddToTypeDatabase for i16 {
469    fn add_to_database(database: &mut TypeDatabase) -> Result<usize> {
470        database.add_integer(Some("i16"), 2, true)
471    }
472}
473
474impl AddToTypeDatabase for i32 {
475    fn add_to_database(database: &mut TypeDatabase) -> Result<usize> {
476        database.add_integer(Some("i32"), 4, true)
477    }
478}
479
480impl AddToTypeDatabase for i64 {
481    fn add_to_database(database: &mut TypeDatabase) -> Result<usize> {
482        database.add_integer(Some("i64"), 8, true)
483    }
484}
485
486impl<T: AddToTypeDatabase, const N: usize> AddToTypeDatabase for [T; N] {
487    fn add_to_database(database: &mut TypeDatabase) -> Result<usize> {
488        let type_id = T::add_to_database(database)?;
489        database.add_array(
490            Some(std::any::type_name::<[T; N]>()),
491            type_id,
492            N.try_into()?,
493        )
494    }
495}