Skip to main content

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 let Some(value) = &self.value {
146            write_i32(stream, value.len() as i32)?;
147            process_encode_io_result(stream.write_all(value))
148        } else {
149            write_i32(stream, -1)
150        }
151    }
152}
153
154impl SimpleBinaryDecodable for ByteString {
155    fn decode<S: Read + ?Sized>(
156        stream: &mut S,
157        decoding_options: &DecodingOptions,
158    ) -> EncodingResult<Self> {
159        let len = read_i32(stream)?;
160        // Null string?
161        if len == -1 {
162            Ok(ByteString::null())
163        } else if len < -1 {
164            Err(Error::decoding(format!(
165                "ByteString buf length is a negative number {len}"
166            )))
167        } else if len as usize > decoding_options.max_byte_string_length {
168            Err(Error::decoding(format!(
169                "ByteString length {} exceeds decoding limit {}",
170                len, decoding_options.max_byte_string_length
171            )))
172        } else {
173            // Create a buffer filled with zeroes and read the byte string over the top
174            let mut buf: Vec<u8> = vec![0u8; len as usize];
175            process_decode_io_result(stream.read_exact(&mut buf))?;
176            Ok(ByteString { value: Some(buf) })
177        }
178    }
179}
180
181impl<'a, T> From<&'a T> for ByteString
182where
183    T: AsRef<[u8]> + ?Sized,
184{
185    fn from(value: &'a T) -> Self {
186        Self::from(value.as_ref().to_vec())
187    }
188}
189
190impl From<Vec<u8>> for ByteString {
191    fn from(value: Vec<u8>) -> Self {
192        // Empty bytes will be treated as Some([])
193        ByteString { value: Some(value) }
194    }
195}
196
197impl From<Guid> for ByteString {
198    fn from(value: Guid) -> Self {
199        ByteString::from(value.as_bytes().to_vec())
200    }
201}
202
203impl TryFrom<&ByteString> for Guid {
204    type Error = ();
205
206    fn try_from(value: &ByteString) -> Result<Self, Self::Error> {
207        if value.is_null_or_empty() {
208            Err(())
209        } else {
210            let bytes = value.as_ref();
211            if bytes.len() != 16 {
212                Err(())
213            } else {
214                let mut guid = [0u8; 16];
215                guid.copy_from_slice(bytes);
216                Ok(Guid::from_bytes(guid))
217            }
218        }
219    }
220}
221
222impl From<ByteString> for String {
223    fn from(value: ByteString) -> Self {
224        value.as_base64()
225    }
226}
227
228impl Default for ByteString {
229    fn default() -> Self {
230        ByteString::null()
231    }
232}
233
234impl ByteString {
235    /// Create a null string (not the same as an empty string)
236    pub fn null() -> ByteString {
237        ByteString { value: None }
238    }
239
240    /// Test if the string is null
241    pub fn is_null(&self) -> bool {
242        self.value.is_none()
243    }
244
245    /// Test if the bytestring has an empty value (not the same as null)
246    pub fn is_empty(&self) -> bool {
247        if let Some(v) = &self.value {
248            v.is_empty()
249        } else {
250            false
251        }
252    }
253
254    /// Test if the string is null or empty
255    pub fn is_null_or_empty(&self) -> bool {
256        self.is_null() || self.is_empty()
257    }
258
259    /// Creates a byte string from a base64 encoded string
260    pub fn from_base64(data: &str) -> Option<ByteString> {
261        if let Ok(bytes) = STANDARD.decode(data) {
262            Some(Self::from(bytes))
263        } else {
264            None
265        }
266    }
267
268    /// Creates a byte string from a base64 encoded string, ignoring whitespace.
269    pub fn from_base64_ignore_whitespace(mut data: String) -> Option<ByteString> {
270        data.retain(|c| !c.is_whitespace());
271        if let Ok(bytes) = STANDARD.decode(&data) {
272            Some(Self::from(bytes))
273        } else {
274            None
275        }
276    }
277
278    /// Encodes the bytestring as a base64 encoded string
279    pub fn as_base64(&self) -> String {
280        // Base64 encodes the byte string so it can be represented as a string
281        if let Some(ref value) = self.value {
282            STANDARD.encode(value)
283        } else {
284            STANDARD.encode("")
285        }
286    }
287
288    /// This function is meant for use with NumericRange. It creates a substring from this string
289    /// from min up to and inclusive of max. Note that min must have an index within the string
290    /// but max is allowed to be beyond the end in which case the remainder of the string is
291    /// returned (see docs for NumericRange).
292    pub fn substring(&self, min: usize, max: usize) -> Result<ByteString, OutOfRange> {
293        if let Some(ref v) = self.value {
294            if min >= v.len() {
295                Err(OutOfRange)
296            } else {
297                let max = if max >= v.len() { v.len() - 1 } else { max };
298                let v = v[min..=max].to_vec();
299                Ok(ByteString::from(v))
300            }
301        } else {
302            Err(OutOfRange)
303        }
304    }
305
306    /// Length of the byte string in bytes.
307    pub fn len(&self) -> usize {
308        if let Some(ref v) = self.value {
309            v.len()
310        } else {
311            0
312        }
313    }
314}
315
316#[test]
317fn bytestring_null() {
318    let v = ByteString::null();
319    assert!(v.is_null());
320}
321
322#[test]
323fn bytestring_empty() {
324    let v = ByteString::from(&[]);
325    assert!(!v.is_null());
326    assert!(v.is_null_or_empty());
327    assert!(v.is_empty());
328}
329
330#[test]
331fn bytestring_bytes() {
332    let a = [0x1u8, 0x2u8, 0x3u8, 0x4u8];
333    let v = ByteString::from(&a);
334    assert!(!v.is_null());
335    assert!(!v.is_empty());
336    assert_eq!(v.value.as_ref().unwrap(), &a);
337}
338
339#[test]
340fn bytestring_substring() {
341    let a = [0x1u8, 0x2u8, 0x3u8, 0x4u8];
342    let v = ByteString::from(&a);
343    let v2 = v.substring(2, 10000).unwrap();
344    let a2 = v2.value.as_ref().unwrap().as_slice();
345    assert_eq!(a2, &a[2..]);
346
347    let v2 = v.substring(2, 2).unwrap();
348    let a2 = v2.value.as_ref().unwrap().as_slice();
349    assert_eq!(a2, &a[2..3]);
350
351    let v2 = v.substring(0, 2000).unwrap();
352    assert_eq!(v, v2);
353    assert_eq!(v2.value.as_ref().unwrap(), &a);
354
355    assert!(v.substring(4, 10000).is_err());
356    assert!(ByteString::null().substring(0, 0).is_err());
357}