Skip to main content

steamkit/util/
stream_helpers.rs

1//! Async read/write helpers for tokio streams: little-endian numbers and
2//! null-terminated strings.
3
4use async_trait::async_trait;
5use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
6
7use crate::error::SteamError;
8
9/// The text encodings the null-terminated string helpers support.
10#[derive(Clone, Copy, Debug, PartialEq, Eq)]
11pub enum StreamEncoding {
12    /// UTF-8, which Steam uses nearly everywhere.
13    Utf8,
14    /// US-ASCII; bytes above `0x7F` are replaced with `?`.
15    Ascii,
16    /// ISO-8859-1 (Latin-1); code points above `U+00FF` are replaced with `?`.
17    Latin1,
18}
19
20impl StreamEncoding {
21    /// How many bytes one character takes (1 for everything but UTF-8, which
22    /// is handled separately).
23    fn character_size(self) -> usize {
24        1
25    }
26
27    /// Encodes `value`, without a terminator.
28    fn encode(self, value: &str) -> Vec<u8> {
29        match self {
30            StreamEncoding::Utf8 => value.as_bytes().to_vec(),
31            StreamEncoding::Ascii => value
32                .chars()
33                .map(|character| {
34                    if (character as u32) < 0x80 {
35                        character as u8
36                    } else {
37                        b'?'
38                    }
39                })
40                .collect(),
41            StreamEncoding::Latin1 => value
42                .chars()
43                .map(|character| {
44                    if (character as u32) < 0x100 {
45                        character as u8
46                    } else {
47                        b'?'
48                    }
49                })
50                .collect(),
51        }
52    }
53
54    /// Decodes `bytes`.
55    fn decode(self, bytes: &[u8]) -> String {
56        match self {
57            StreamEncoding::Utf8 => String::from_utf8_lossy(bytes).into_owned(),
58            StreamEncoding::Ascii => bytes
59                .iter()
60                .map(|byte| {
61                    if *byte < 0x80 {
62                        *byte as char
63                    } else {
64                        '?'
65                    }
66                })
67                .collect(),
68            StreamEncoding::Latin1 => bytes.iter().map(|byte| *byte as char).collect(),
69        }
70    }
71}
72
73/// Async read helpers.
74#[async_trait]
75pub trait StreamReadHelpers: AsyncRead + Send + Unpin {
76    /// Reads a little endian 16 bit signed integer.
77    async fn read_int16(&mut self) -> Result<i16, SteamError> {
78        let mut data = [0u8; 2];
79        self.read_exact(&mut data).await?;
80        Ok(i16::from_le_bytes(data))
81    }
82
83    /// Reads a little endian 16 bit unsigned integer.
84    async fn read_uint16(&mut self) -> Result<u16, SteamError> {
85        let mut data = [0u8; 2];
86        self.read_exact(&mut data).await?;
87        Ok(u16::from_le_bytes(data))
88    }
89
90    /// Reads a little endian 32 bit signed integer.
91    async fn read_int32(&mut self) -> Result<i32, SteamError> {
92        let mut data = [0u8; 4];
93        self.read_exact(&mut data).await?;
94        Ok(i32::from_le_bytes(data))
95    }
96
97    /// Reads a little endian 64 bit signed integer.
98    async fn read_int64(&mut self) -> Result<i64, SteamError> {
99        let mut data = [0u8; 8];
100        self.read_exact(&mut data).await?;
101        Ok(i64::from_le_bytes(data))
102    }
103
104    /// Reads a little endian 32 bit unsigned integer.
105    async fn read_uint32(&mut self) -> Result<u32, SteamError> {
106        let mut data = [0u8; 4];
107        self.read_exact(&mut data).await?;
108        Ok(u32::from_le_bytes(data))
109    }
110
111    /// Reads a little endian 64 bit unsigned integer.
112    async fn read_uint64(&mut self) -> Result<u64, SteamError> {
113        let mut data = [0u8; 8];
114        self.read_exact(&mut data).await?;
115        Ok(u64::from_le_bytes(data))
116    }
117
118    /// Reads a little endian 32 bit floating point number.
119    async fn read_float(&mut self) -> Result<f32, SteamError> {
120        let mut data = [0u8; 4];
121        self.read_exact(&mut data).await?;
122        Ok(f32::from_le_bytes(data))
123    }
124
125    /// Reads a null-terminated string in the given encoding, stopping at the
126    /// terminator (which isn't included) or the end of the stream.
127    async fn read_null_term_string(
128        &mut self,
129        encoding: StreamEncoding,
130    ) -> Result<String, SteamError> {
131        let mut result: Vec<u8> = Vec::new();
132
133        if encoding == StreamEncoding::Utf8 {
134            // UTF-8: read one byte at a time, since the terminator is a single
135            // zero byte.
136            loop {
137                let mut byte = [0u8; 1];
138
139                let read = self.read(&mut byte).await?;
140
141                if read == 0 || byte[0] == 0 {
142                    break;
143                }
144
145                result.push(byte[0]);
146            }
147        } else {
148            let character_size = encoding.character_size();
149            let mut chunk = vec![0u8; character_size];
150
151            loop {
152                chunk.fill(0);
153
154                // Read a whole character, unless the stream ends first.
155                let mut read = 0;
156
157                while read < chunk.len() {
158                    let count = self.read(&mut chunk[read..]).await?;
159
160                    if count == 0 {
161                        break;
162                    }
163
164                    read += count;
165                }
166
167                if read == 0 {
168                    break;
169                }
170
171                if encoding.decode(&chunk) == "\0" {
172                    break;
173                }
174
175                result.extend_from_slice(&chunk);
176            }
177        }
178
179        Ok(encoding.decode(&result))
180    }
181
182    /// Reads until `buffer` is full or the stream ends, and returns how many
183    /// bytes were read.
184    async fn read_all(&mut self, buffer: &mut [u8]) -> Result<usize, SteamError> {
185        let mut read = 0;
186
187        while read < buffer.len() {
188            let count = self.read(&mut buffer[read..]).await?;
189
190            if count == 0 {
191                break;
192            }
193
194            read += count;
195        }
196
197        Ok(read)
198    }
199}
200
201/// Async write helpers.
202#[async_trait]
203pub trait StreamWriteHelpers: AsyncWrite + Send + Unpin {
204    /// Writes `value` followed by a null terminator.
205    async fn write_null_term_string(
206        &mut self,
207        value: &str,
208        encoding: StreamEncoding,
209    ) -> Result<(), SteamError> {
210        let mut bytes = encoding.encode(value);
211        bytes.push(0);
212
213        self.write_all(&bytes).await?;
214        self.flush().await?;
215
216        Ok(())
217    }
218}
219
220impl<R> StreamReadHelpers for R where R: AsyncRead + Send + Unpin {}
221
222impl<W> StreamWriteHelpers for W where W: AsyncWrite + Send + Unpin {}