opcua_types/
encoding.rs

1// OPCUA for Rust
2// SPDX-License-Identifier: MPL-2.0
3// Copyright (C) 2017-2022 Adam Lock
4
5//! Contains the `BinaryEncoder` trait and helpers for reading and writing of scalar values and
6//! other primitives.
7
8use std::{
9    fmt::Debug,
10    io::{Cursor, Read, Result, Write},
11};
12
13use byteorder::{ByteOrder, LittleEndian, WriteBytesExt};
14use chrono::Duration;
15
16use crate::{constants, status_codes::StatusCode};
17
18pub type EncodingResult<T> = std::result::Result<T, StatusCode>;
19
20#[derive(Clone, Copy, Debug)]
21pub struct DecodingOptions {
22    /// Time offset between the client and the server, only used by the client when it's configured
23    /// to ignore time skew.
24    pub client_offset: Duration,
25    /// Maximum size of a message chunk in bytes. 0 means no limit
26    pub max_chunk_count: usize,
27    /// Maximum length in bytes (not chars!) of a string. 0 actually means 0, i.e. no string permitted
28    pub max_string_length: usize,
29    /// Maximum length in bytes of a byte string. 0 actually means 0, i.e. no byte string permitted
30    pub max_byte_string_length: usize,
31    /// Maximum number of array elements. 0 actually means 0, i.e. no array permitted
32    pub max_array_length: usize,
33}
34
35impl Default for DecodingOptions {
36    fn default() -> Self {
37        DecodingOptions {
38            client_offset: Duration::zero(),
39            max_chunk_count: 0,
40            max_string_length: constants::MAX_STRING_LENGTH,
41            max_byte_string_length: constants::MAX_BYTE_STRING_LENGTH,
42            max_array_length: constants::MAX_ARRAY_LENGTH,
43        }
44    }
45}
46
47impl DecodingOptions {
48    /// This can be useful for decoding extension objects where the payload is not expected to contain
49    /// any string or array.
50    pub fn minimal() -> Self {
51        DecodingOptions {
52            client_offset: Duration::zero(),
53            max_chunk_count: 0,
54            max_string_length: 0,
55            max_byte_string_length: 0,
56            max_array_length: 0,
57        }
58    }
59}
60
61/// OPC UA Binary Encoding interface. Anything that encodes to binary must implement this. It provides
62/// functions to calculate the size in bytes of the struct (for allocating memory), encoding to a stream
63/// and decoding from a stream.
64pub trait BinaryEncoder<T> {
65    /// Returns the exact byte length of the structure as it would be if `encode` were called.
66    /// This may be called prior to writing to ensure the correct amount of space is available.
67    fn byte_len(&self) -> usize;
68    /// Encodes the instance to the write stream.
69    fn encode<S: Write>(&self, stream: &mut S) -> EncodingResult<usize>;
70    /// Decodes an instance from the read stream. The decoding options contains restrictions set by
71    /// the server / client on the length of strings, arrays etc. If these limits are exceeded the
72    /// implementation should return with a `BadDecodingError` as soon as possible.
73    fn decode<S: Read>(stream: &mut S, decoding_options: &DecodingOptions) -> EncodingResult<T>;
74
75    // Convenience method for encoding a message straight into an array of bytes. It is preferable to reuse buffers than
76    // to call this so it should be reserved for tests and trivial code.
77    fn encode_to_vec(&self) -> Vec<u8> {
78        let mut buffer = Cursor::new(Vec::with_capacity(self.byte_len()));
79        let _ = self.encode(&mut buffer);
80        buffer.into_inner()
81    }
82}
83
84/// Converts an IO encoding error (and logs when in error) into an EncodingResult
85pub fn process_encode_io_result(result: Result<usize>) -> EncodingResult<usize> {
86    result.map_err(|err| {
87        trace!("Encoding error - {:?}", err);
88        StatusCode::BadEncodingError
89    })
90}
91
92/// Converts an IO encoding error (and logs when in error) into an EncodingResult
93pub fn process_decode_io_result<T>(result: Result<T>) -> EncodingResult<T>
94where
95    T: Debug,
96{
97    result.map_err(|err| {
98        trace!("Decoding error - {:?}", err);
99        StatusCode::BadDecodingError
100    })
101}
102
103/// Calculates the length in bytes of an array of encoded type
104pub fn byte_len_array<T: BinaryEncoder<T>>(values: &Option<Vec<T>>) -> usize {
105    let mut size = 4;
106    if let Some(ref values) = values {
107        size += values.iter().map(|v| v.byte_len()).sum::<usize>();
108    }
109    size
110}
111
112/// Write an array of the encoded type to stream, preserving distinction between null array and empty array
113pub fn write_array<S: Write, T: BinaryEncoder<T>>(
114    stream: &mut S,
115    values: &Option<Vec<T>>,
116) -> EncodingResult<usize> {
117    let mut size = 0;
118    if let Some(ref values) = values {
119        size += write_i32(stream, values.len() as i32)?;
120        for value in values.iter() {
121            size += value.encode(stream)?;
122        }
123    } else {
124        size += write_i32(stream, -1)?;
125    }
126    Ok(size)
127}
128
129/// Reads an array of the encoded type from a stream, preserving distinction between null array and empty array
130pub fn read_array<S: Read, T: BinaryEncoder<T>>(
131    stream: &mut S,
132    decoding_options: &DecodingOptions,
133) -> EncodingResult<Option<Vec<T>>> {
134    let len = read_i32(stream)?;
135    if len == -1 {
136        Ok(None)
137    } else if len < -1 {
138        error!("Array length is negative value and invalid");
139        Err(StatusCode::BadDecodingError)
140    } else if len as usize > decoding_options.max_array_length {
141        error!(
142            "Array length {} exceeds decoding limit {}",
143            len, decoding_options.max_array_length
144        );
145        Err(StatusCode::BadDecodingError)
146    } else {
147        let mut values: Vec<T> = Vec::with_capacity(len as usize);
148        for _ in 0..len {
149            values.push(T::decode(stream, decoding_options)?);
150        }
151        Ok(Some(values))
152    }
153}
154
155/// Writes a series of identical bytes to the stream
156pub fn write_bytes(stream: &mut dyn Write, value: u8, count: usize) -> EncodingResult<usize> {
157    for _ in 0..count {
158        let _ = stream
159            .write_u8(value)
160            .map_err(|_| StatusCode::BadEncodingError)?;
161    }
162    Ok(count)
163}
164
165/// Writes an unsigned byte to the stream
166pub fn write_u8<T>(stream: &mut dyn Write, value: T) -> EncodingResult<usize>
167where
168    T: Into<u8>,
169{
170    let buf: [u8; 1] = [value.into()];
171    process_encode_io_result(stream.write(&buf))
172}
173
174/// Writes a signed 16-bit value to the stream
175pub fn write_i16<T>(stream: &mut dyn Write, value: T) -> EncodingResult<usize>
176where
177    T: Into<i16>,
178{
179    let mut buf = [0u8; 2];
180    LittleEndian::write_i16(&mut buf, value.into());
181    process_encode_io_result(stream.write(&buf))
182}
183
184/// Writes an unsigned 16-bit value to the stream
185pub fn write_u16<T>(stream: &mut dyn Write, value: T) -> EncodingResult<usize>
186where
187    T: Into<u16>,
188{
189    let mut buf = [0u8; 2];
190    LittleEndian::write_u16(&mut buf, value.into());
191    process_encode_io_result(stream.write(&buf))
192}
193
194/// Writes a signed 32-bit value to the stream
195pub fn write_i32<T>(stream: &mut dyn Write, value: T) -> EncodingResult<usize>
196where
197    T: Into<i32>,
198{
199    let mut buf = [0u8; 4];
200    LittleEndian::write_i32(&mut buf, value.into());
201    process_encode_io_result(stream.write(&buf))
202}
203
204/// Writes an unsigned 32-bit value to the stream
205pub fn write_u32<T>(stream: &mut dyn Write, value: T) -> EncodingResult<usize>
206where
207    T: Into<u32>,
208{
209    let mut buf = [0u8; 4];
210    LittleEndian::write_u32(&mut buf, value.into());
211    process_encode_io_result(stream.write(&buf))
212}
213
214/// Writes a signed 64-bit value to the stream
215pub fn write_i64<T>(stream: &mut dyn Write, value: T) -> EncodingResult<usize>
216where
217    T: Into<i64>,
218{
219    let mut buf = [0u8; 8];
220    LittleEndian::write_i64(&mut buf, value.into());
221    process_encode_io_result(stream.write(&buf))
222}
223
224/// Writes an unsigned 64-bit value to the stream
225pub fn write_u64<T>(stream: &mut dyn Write, value: T) -> EncodingResult<usize>
226where
227    T: Into<u64>,
228{
229    let mut buf = [0u8; 8];
230    LittleEndian::write_u64(&mut buf, value.into());
231    process_encode_io_result(stream.write(&buf))
232}
233
234/// Writes a 32-bit precision value to the stream
235pub fn write_f32<T>(stream: &mut dyn Write, value: T) -> EncodingResult<usize>
236where
237    T: Into<f32>,
238{
239    let mut buf = [0u8; 4];
240    LittleEndian::write_f32(&mut buf, value.into());
241    process_encode_io_result(stream.write(&buf))
242}
243
244/// Writes a 64-bit precision value to the stream
245pub fn write_f64<T>(stream: &mut dyn Write, value: T) -> EncodingResult<usize>
246where
247    T: Into<f64>,
248{
249    let mut buf = [0u8; 8];
250    LittleEndian::write_f64(&mut buf, value.into());
251    process_encode_io_result(stream.write(&buf))
252}
253
254/// Reads an array of bytes from the stream
255pub fn read_bytes(stream: &mut dyn Read, buf: &mut [u8]) -> EncodingResult<usize> {
256    let result = stream.read_exact(buf);
257    process_decode_io_result(result)?;
258    Ok(buf.len())
259}
260
261/// Read an unsigned byte from the stream
262pub fn read_u8(stream: &mut dyn Read) -> EncodingResult<u8> {
263    let mut buf = [0u8];
264    let result = stream.read_exact(&mut buf);
265    process_decode_io_result(result)?;
266    Ok(buf[0])
267}
268
269/// Read an signed 16-bit value from the stream
270pub fn read_i16(stream: &mut dyn Read) -> EncodingResult<i16> {
271    let mut buf = [0u8; 2];
272    let result = stream.read_exact(&mut buf);
273    process_decode_io_result(result)?;
274    Ok(LittleEndian::read_i16(&buf))
275}
276
277/// Read an unsigned 16-bit value from the stream
278pub fn read_u16(stream: &mut dyn Read) -> EncodingResult<u16> {
279    let mut buf = [0u8; 2];
280    let result = stream.read_exact(&mut buf);
281    process_decode_io_result(result)?;
282    Ok(LittleEndian::read_u16(&buf))
283}
284
285/// Read a signed 32-bit value from the stream
286pub fn read_i32(stream: &mut dyn Read) -> EncodingResult<i32> {
287    let mut buf = [0u8; 4];
288    let result = stream.read_exact(&mut buf);
289    process_decode_io_result(result)?;
290    Ok(LittleEndian::read_i32(&buf))
291}
292
293/// Read an unsigned 32-bit value from the stream
294pub fn read_u32(stream: &mut dyn Read) -> EncodingResult<u32> {
295    let mut buf = [0u8; 4];
296    let result = stream.read_exact(&mut buf);
297    process_decode_io_result(result)?;
298    Ok(LittleEndian::read_u32(&buf))
299}
300
301/// Read a signed 64-bit value from the stream
302pub fn read_i64(stream: &mut dyn Read) -> EncodingResult<i64> {
303    let mut buf = [0u8; 8];
304    let result = stream.read_exact(&mut buf);
305    process_decode_io_result(result)?;
306    Ok(LittleEndian::read_i64(&buf))
307}
308
309/// Read an unsigned 64-bit value from the stream
310pub fn read_u64(stream: &mut dyn Read) -> EncodingResult<u64> {
311    let mut buf = [0u8; 8];
312    let result = stream.read_exact(&mut buf);
313    process_decode_io_result(result)?;
314    Ok(LittleEndian::read_u64(&buf))
315}
316
317/// Read a 32-bit precision value from the stream
318pub fn read_f32(stream: &mut dyn Read) -> EncodingResult<f32> {
319    let mut buf = [0u8; 4];
320    let result = stream.read_exact(&mut buf);
321    process_decode_io_result(result)?;
322    Ok(LittleEndian::read_f32(&buf))
323}
324
325/// Read a 64-bit precision from the stream
326pub fn read_f64(stream: &mut dyn Read) -> EncodingResult<f64> {
327    let mut buf = [0u8; 8];
328    let result = stream.read_exact(&mut buf);
329    process_decode_io_result(result)?;
330    Ok(LittleEndian::read_f64(&buf))
331}