Skip to main content

opcua/types/
array.rs

1use crate::types::{variant::*, variant_type_id::*, StatusCode};
2
3/// An array is a vector of values with an optional number of dimensions.
4/// It is expected that the multi-dimensional array is valid, or it might not be encoded or decoded
5/// properly. The dimensions should match the number of values, or the array is invalid.
6#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
7pub struct Array {
8    // Type of elements in the array
9    pub value_type: VariantTypeId,
10
11    /// Values are stored sequentially
12    pub values: Vec<Variant>,
13
14    /// Multi dimension array which can contain any scalar type, all the same type. Nested
15    /// arrays are rejected. Higher rank dimensions are serialized first. For example an array
16    /// with dimensions [2,2,2] is written in this order - [0,0,0], [0,0,1], [0,1,0], [0,1,1],
17    /// [1,0,0], [1,0,1], [1,1,0], [1,1,1].
18    pub dimensions: Option<Vec<u32>>,
19}
20
21impl Array {
22    /// Constructs a single dimension array from the supplied values
23    pub fn new<V>(value_type: VariantTypeId, values: V) -> Result<Array, StatusCode>
24    where
25        V: Into<Vec<Variant>>,
26    {
27        let values = values.into();
28        if Self::validate_array_type_to_values(value_type, &values) {
29            Ok(Array {
30                value_type,
31                values,
32                dimensions: None,
33            })
34        } else {
35            Err(StatusCode::BadDecodingError)
36        }
37    }
38
39    /// Constructs a multi-dimensional array from the supplied values. The values are still provided
40    /// and held as a single dimension array but a separate dimensions parameter indicates how the
41    /// values are accessed.
42    pub fn new_multi<V, D>(
43        value_type: VariantTypeId,
44        values: V,
45        dimensions: D,
46    ) -> Result<Array, StatusCode>
47    where
48        V: Into<Vec<Variant>>,
49        D: Into<Vec<u32>>,
50    {
51        let values = values.into();
52        let dimensions = dimensions.into();
53
54        // TODO should also Self::validate_dimensions(values.len(), &dimensions)
55        if Self::validate_array_type_to_values(value_type, &values) {
56            Ok(Array {
57                value_type,
58                values,
59                dimensions: Some(dimensions),
60            })
61        } else {
62            Err(StatusCode::BadDecodingError)
63        }
64    }
65
66    /// This is a runtime check to ensure the type of the array also matches the types of the variants in the array.
67    fn validate_array_type_to_values(value_type: VariantTypeId, values: &[Variant]) -> bool {
68        match value_type {
69            VariantTypeId::Array | VariantTypeId::Empty => {
70                error!("Invalid array type supplied");
71                false
72            }
73            _ => {
74                if !values_are_of_type(values, value_type) {
75                    // If the values exist, then validate them to the type
76                    error!("Value type of array does not match contents");
77                    false
78                } else {
79                    true
80                }
81            }
82        }
83    }
84
85    pub fn is_valid(&self) -> bool {
86        self.is_valid_dimensions() && Self::array_is_valid(&self.values)
87    }
88
89    pub fn encoding_mask(&self) -> u8 {
90        let mut encoding_mask = self.value_type.encoding_mask();
91        encoding_mask |= EncodingMask::ARRAY_VALUES_BIT;
92        if self.dimensions.is_some() {
93            encoding_mask |= EncodingMask::ARRAY_DIMENSIONS_BIT;
94        }
95        encoding_mask
96    }
97
98    /// Tests that the variants in the slice all have the same variant type
99    fn array_is_valid(values: &[Variant]) -> bool {
100        if values.is_empty() {
101            true
102        } else {
103            let expected_type_id = values[0].type_id();
104            if expected_type_id == VariantTypeId::Array {
105                // Nested arrays are explicitly NOT allowed
106                error!("Variant array contains nested array {:?}", expected_type_id);
107                false
108            } else if values.len() > 1 {
109                values_are_of_type(&values[1..], expected_type_id)
110            } else {
111                // Only contains 1 element
112                true
113            }
114        }
115    }
116
117    fn validate_dimensions(values_len: usize, dimensions: &[u32]) -> bool {
118        // Check that the array dimensions match the length of the array
119        let mut length: usize = 1;
120        for d in dimensions {
121            // Check for invalid dimensions
122            if *d == 0 {
123                // This dimension has no fixed size, so skip it
124                continue;
125            }
126            length *= *d as usize;
127        }
128        length <= values_len
129    }
130
131    fn is_valid_dimensions(&self) -> bool {
132        if let Some(ref dimensions) = self.dimensions {
133            Self::validate_dimensions(self.values.len(), dimensions)
134        } else {
135            true
136        }
137    }
138}
139
140/// Check that all elements in the slice of arrays are the same type.
141pub fn values_are_of_type(values: &[Variant], expected_type: VariantTypeId) -> bool {
142    // Ensure all remaining elements are the same type as the first element
143    let found_unexpected = values.iter().any(|v| v.type_id() != expected_type);
144    if found_unexpected {
145        error!(
146            "Variant array's type is expected to be {:?} but found other types in it",
147            expected_type
148        );
149    };
150    !found_unexpected
151}