opcua_types/
byte_string.rs

1// OPCUA for Rust
2// SPDX-License-Identifier: MPL-2.0
3// Copyright (C) 2017-2024 Adam Lock
4
5//! Contains the implementation of `ByteString`.
6
7use std::{
8    convert::TryFrom,
9    io::{Read, Write},
10};
11
12use base64::{engine::general_purpose::STANDARD, Engine};
13
14use crate::{
15    encoding::{process_decode_io_result, process_encode_io_result, write_i32, EncodingResult},
16    read_i32, DecodingOptions, Error, Guid, OutOfRange, SimpleBinaryDecodable,
17    SimpleBinaryEncodable, UaNullable,
18};
19
20/// A sequence of octets.
21#[derive(Eq, PartialEq, Debug, Clone, Hash)]
22pub struct ByteString {
23    /// Raw inner byte string values as an array of bytes.
24    pub value: Option<Vec<u8>>,
25}
26
27impl AsRef<[u8]> for ByteString {
28    fn as_ref(&self) -> &[u8] {
29        match self.value.as_ref() {
30            Some(v) => v.as_slice(),
31            None => &[],
32        }
33    }
34}
35
36impl PartialEq<&[u8]> for ByteString {
37    fn eq(&self, other: &&[u8]) -> bool {
38        self.as_ref() == *other
39    }
40}
41
42impl PartialEq<[u8]> for ByteString {
43    fn eq(&self, other: &[u8]) -> bool {
44        self.as_ref() == other
45    }
46}
47
48impl UaNullable for ByteString {
49    fn is_ua_null(&self) -> bool {
50        self.is_null()
51    }
52}
53
54#[cfg(feature = "json")]
55mod json {
56    use std::io::{Read, Write};
57
58    use crate::{json::*, Error};
59
60    use super::ByteString;
61
62    impl JsonEncodable for ByteString {
63        fn encode(
64            &self,
65            stream: &mut JsonStreamWriter<&mut dyn Write>,
66            _ctx: &crate::json::Context<'_>,
67        ) -> crate::EncodingResult<()> {
68            if self.value.is_some() {
69                stream.string_value(&self.as_base64())?;
70            } else {
71                stream.null_value()?;
72            }
73            Ok(())
74        }
75    }
76
77    impl JsonDecodable for ByteString {
78        fn decode(
79            stream: &mut JsonStreamReader<&mut dyn Read>,
80            _ctx: &Context<'_>,
81        ) -> crate::EncodingResult<Self> {
82            match stream.peek()? {
83                ValueType::String => Ok(Self::from_base64_ignore_whitespace(stream.next_string()?)
84                    .ok_or_else(|| Error::decoding("Cannot decode base64 bytestring"))?),
85                _ => {
86                    stream.next_null()?;
87                    Ok(Self::null())
88                }
89            }
90        }
91    }
92}
93
94#[cfg(feature = "xml")]
95mod xml {
96    use crate::xml::*;
97    use std::io::{Read, Write};
98
99    use super::ByteString;
100
101    impl XmlType for ByteString {
102        const TAG: &'static str = "ByteString";
103    }
104
105    impl XmlEncodable for ByteString {
106        fn encode(
107            &self,
108            writer: &mut XmlStreamWriter<&mut dyn Write>,
109            _context: &Context<'_>,
110        ) -> EncodingResult<()> {
111            if self.value.is_some() {
112                writer.write_text(&self.as_base64())?;
113            }
114            Ok(())
115        }
116    }
117
118    impl XmlDecodable for ByteString {
119        fn decode(
120            read: &mut XmlStreamReader<&mut dyn Read>,
121            _context: &Context<'_>,
122        ) -> Result<Self, Error> {
123            let s = read.consume_as_text()?;
124            if s.is_empty() {
125                Ok(ByteString::null())
126            } else {
127                Ok(ByteString::from_base64_ignore_whitespace(s)
128                    .ok_or_else(|| Error::decoding("Cannot decode base64 bytestring"))?)
129            }
130        }
131    }
132}
133
134impl SimpleBinaryEncodable for ByteString {
135    fn byte_len(&self) -> usize {
136        // Length plus the actual length of bytes (if not null)
137        4 + match &self.value {
138            Some(v) => v.len(),
139            None => 0,
140        }
141    }
142
143    fn encode<S: Write + ?Sized>(&self, stream: &mut S) -> EncodingResult<()> {
144        // Strings are uncoded as UTF8 chars preceded by an Int32 length. A -1 indicates a null string
145        if self.value.is_none() {
146            write_i32(stream, -1)
147        } else {
148            let value = self.value.as_ref().unwrap();
149            write_i32(stream, value.len() as i32)?;
150            process_encode_io_result(stream.write_all(value))
151        }
152    }
153}
154
155impl SimpleBinaryDecodable for ByteString {
156    fn decode<S: Read + ?Sized>(
157        stream: &mut S,
158        decoding_options: &DecodingOptions,
159    ) -> EncodingResult<Self> {
160        let len = read_i32(stream)?;
161        // Null string?
162        if len == -1 {
163            Ok(ByteString::null())
164        } else if len < -1 {
165            Err(Error::decoding(format!(
166                "ByteString buf length is a negative number {len}"
167            )))
168        } else if len as usize > decoding_options.max_byte_string_length {
169            Err(Error::decoding(format!(
170                "ByteString length {} exceeds decoding limit {}",
171                len, decoding_options.max_byte_string_length
172            )))
173        } else {
174            // Create a buffer filled with zeroes and read the byte string over the top
175            let mut buf: Vec<u8> = vec![0u8; len as usize];
176            process_decode_io_result(stream.read_exact(&mut buf))?;
177            Ok(ByteString { value: Some(buf) })
178        }
179    }
180}
181
182impl<'a, T> From<&'a T> for ByteString
183where
184    T: AsRef<[u8]> + ?Sized,
185{
186    fn from(value: &'a T) -> Self {
187        Self::from(value.as_ref().to_vec())
188    }
189}
190
191impl From<Vec<u8>> for ByteString {
192    fn from(value: Vec<u8>) -> Self {
193        // Empty bytes will be treated as Some([])
194        ByteString { value: Some(value) }
195    }
196}
197
198impl From<Guid> for ByteString {
199    fn from(value: Guid) -> Self {
200        ByteString::from(value.as_bytes().to_vec())
201    }
202}
203
204impl TryFrom<&ByteString> for Guid {
205    type Error = ();
206
207    fn try_from(value: &ByteString) -> Result<Self, Self::Error> {
208        if value.is_null_or_empty() {
209            Err(())
210        } else {
211            let bytes = value.as_ref();
212            if bytes.len() != 16 {
213                Err(())
214            } else {
215                let mut guid = [0u8; 16];
216                guid.copy_from_slice(bytes);
217                Ok(Guid::from_bytes(guid))
218            }
219        }
220    }
221}
222
223impl From<ByteString> for String {
224    fn from(value: ByteString) -> Self {
225        value.as_base64()
226    }
227}
228
229impl Default for ByteString {
230    fn default() -> Self {
231        ByteString::null()
232    }
233}
234
235impl ByteString {
236    /// Create a null string (not the same as an empty string)
237    pub fn null() -> ByteString {
238        ByteString { value: None }
239    }
240
241    /// Test if the string is null
242    pub fn is_null(&self) -> bool {
243        self.value.is_none()
244    }
245
246    /// Test if the bytestring has an empty value (not the same as null)
247    pub fn is_empty(&self) -> bool {
248        if let Some(v) = &self.value {
249            v.is_empty()
250        } else {
251            false
252        }
253    }
254
255    /// Test if the string is null or empty
256    pub fn is_null_or_empty(&self) -> bool {
257        self.is_null() || self.is_empty()
258    }
259
260    /// Creates a byte string from a base64 encoded string
261    pub fn from_base64(data: &str) -> Option<ByteString> {
262        if let Ok(bytes) = STANDARD.decode(data) {
263            Some(Self::from(bytes))
264        } else {
265            None
266        }
267    }
268
269    /// Creates a byte string from a base64 encoded string, ignoring whitespace.
270    pub fn from_base64_ignore_whitespace(mut data: String) -> Option<ByteString> {
271        data.retain(|c| !c.is_whitespace());
272        if let Ok(bytes) = STANDARD.decode(&data) {
273            Some(Self::from(bytes))
274        } else {
275            None
276        }
277    }
278
279    /// Encodes the bytestring as a base64 encoded string
280    pub fn as_base64(&self) -> String {
281        // Base64 encodes the byte string so it can be represented as a string
282        if let Some(ref value) = self.value {
283            STANDARD.encode(value)
284        } else {
285            STANDARD.encode("")
286        }
287    }
288
289    /// This function is meant for use with NumericRange. It creates a substring from this string
290    /// from min up to and inclusive of max. Note that min must have an index within the string
291    /// but max is allowed to be beyond the end in which case the remainder of the string is
292    /// returned (see docs for NumericRange).
293    pub fn substring(&self, min: usize, max: usize) -> Result<ByteString, OutOfRange> {
294        if let Some(ref v) = self.value {
295            if min >= v.len() {
296                Err(OutOfRange)
297            } else {
298                let max = if max >= v.len() { v.len() - 1 } else { max };
299                let v = v[min..=max].to_vec();
300                Ok(ByteString::from(v))
301            }
302        } else {
303            Err(OutOfRange)
304        }
305    }
306
307    /// Length of the byte string in bytes.
308    pub fn len(&self) -> usize {
309        if let Some(ref v) = self.value {
310            v.len()
311        } else {
312            0
313        }
314    }
315}
316
317#[test]
318fn bytestring_null() {
319    let v = ByteString::null();
320    assert!(v.is_null());
321}
322
323#[test]
324fn bytestring_empty() {
325    let v = ByteString::from(&[]);
326    assert!(!v.is_null());
327    assert!(v.is_null_or_empty());
328    assert!(v.is_empty());
329}
330
331#[test]
332fn bytestring_bytes() {
333    let a = [0x1u8, 0x2u8, 0x3u8, 0x4u8];
334    let v = ByteString::from(&a);
335    assert!(!v.is_null());
336    assert!(!v.is_empty());
337    assert_eq!(v.value.as_ref().unwrap(), &a);
338}
339
340#[test]
341fn bytestring_substring() {
342    let a = [0x1u8, 0x2u8, 0x3u8, 0x4u8];
343    let v = ByteString::from(&a);
344    let v2 = v.substring(2, 10000).unwrap();
345    let a2 = v2.value.as_ref().unwrap().as_slice();
346    assert_eq!(a2, &a[2..]);
347
348    let v2 = v.substring(2, 2).unwrap();
349    let a2 = v2.value.as_ref().unwrap().as_slice();
350    assert_eq!(a2, &a[2..3]);
351
352    let v2 = v.substring(0, 2000).unwrap();
353    assert_eq!(v, v2);
354    assert_eq!(v2.value.as_ref().unwrap(), &a);
355
356    assert!(v.substring(4, 10000).is_err());
357    assert!(ByteString::null().substring(0, 0).is_err());
358}