Skip to main content

qubit_io/ext/
string_write_ext.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2026 Haixing Hu.
4 *
5 *    SPDX-License-Identifier: Apache-2.0
6 *
7 *    Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10// qubit-style: allow coverage-cfg
11use std::io::{
12    Error,
13    ErrorKind,
14    Result,
15    Write,
16};
17
18use crate::{
19    BinaryWriteExt,
20    Leb128WriteExt,
21};
22
23/// Extension methods for writing length-prefixed UTF-8 strings.
24pub trait StringWriteExt: Write {
25    /// Writes a UTF-8 string with an unsigned LEB128 byte-length prefix.
26    ///
27    /// # Parameters
28    /// - `value`: String slice to write.
29    ///
30    /// # Errors
31    /// Returns an I/O error from the underlying writer.
32    fn write_utf8_string_uleb(&mut self, value: &str) -> Result<()>;
33
34    /// Writes a UTF-8 string with a big-endian `u16` byte-length prefix.
35    ///
36    /// # Parameters
37    /// - `value`: String slice to write.
38    ///
39    /// # Errors
40    /// Returns [`ErrorKind::InvalidInput`] when the UTF-8 byte length does not
41    /// fit into `u16`, or an I/O error from the underlying writer.
42    fn write_utf8_string_u16_be(&mut self, value: &str) -> Result<()>;
43
44    /// Writes a UTF-8 string with a little-endian `u16` byte-length prefix.
45    ///
46    /// # Parameters
47    /// - `value`: String slice to write.
48    ///
49    /// # Errors
50    /// Returns [`ErrorKind::InvalidInput`] when the UTF-8 byte length does not
51    /// fit into `u16`, or an I/O error from the underlying writer.
52    fn write_utf8_string_u16_le(&mut self, value: &str) -> Result<()>;
53
54    /// Writes a UTF-8 string with a big-endian `u32` byte-length prefix.
55    ///
56    /// # Parameters
57    /// - `value`: String slice to write.
58    ///
59    /// # Errors
60    /// Returns [`ErrorKind::InvalidInput`] when the UTF-8 byte length does not
61    /// fit into `u32`, or an I/O error from the underlying writer.
62    fn write_utf8_string_u32_be(&mut self, value: &str) -> Result<()>;
63
64    /// Writes a UTF-8 string with a little-endian `u32` byte-length prefix.
65    ///
66    /// # Parameters
67    /// - `value`: String slice to write.
68    ///
69    /// # Errors
70    /// Returns [`ErrorKind::InvalidInput`] when the UTF-8 byte length does not
71    /// fit into `u32`, or an I/O error from the underlying writer.
72    fn write_utf8_string_u32_le(&mut self, value: &str) -> Result<()>;
73}
74
75impl<T> StringWriteExt for T
76where
77    T: Write + ?Sized,
78{
79    #[inline]
80    fn write_utf8_string_uleb(&mut self, value: &str) -> Result<()> {
81        let mut writer = self;
82        write_utf8_string_uleb_to(&mut writer, value)
83    }
84
85    #[inline]
86    fn write_utf8_string_u16_be(&mut self, value: &str) -> Result<()> {
87        let mut writer = self;
88        write_utf8_string_u16_be_to(&mut writer, value)
89    }
90
91    #[inline]
92    fn write_utf8_string_u16_le(&mut self, value: &str) -> Result<()> {
93        let mut writer = self;
94        write_utf8_string_u16_le_to(&mut writer, value)
95    }
96
97    #[inline]
98    fn write_utf8_string_u32_be(&mut self, value: &str) -> Result<()> {
99        let mut writer = self;
100        write_utf8_string_u32_be_to(&mut writer, value)
101    }
102
103    #[inline]
104    fn write_utf8_string_u32_le(&mut self, value: &str) -> Result<()> {
105        let mut writer = self;
106        write_utf8_string_u32_le_to(&mut writer, value)
107    }
108}
109
110fn write_utf8_string_uleb_to(writer: &mut dyn Write, value: &str) -> Result<()> {
111    let bytes = value.as_bytes();
112    writer.write_uleb_usize(bytes.len())?;
113    writer.write_all(bytes)
114}
115
116fn write_utf8_string_u16_be_to(writer: &mut dyn Write, value: &str) -> Result<()> {
117    let bytes = value.as_bytes();
118    write_utf8_bytes_u16_be(writer, bytes, bytes.len())
119}
120
121fn write_utf8_string_u16_le_to(writer: &mut dyn Write, value: &str) -> Result<()> {
122    let bytes = value.as_bytes();
123    write_utf8_bytes_u16_le(writer, bytes, bytes.len())
124}
125
126fn write_utf8_string_u32_be_to(writer: &mut dyn Write, value: &str) -> Result<()> {
127    let bytes = value.as_bytes();
128    write_utf8_bytes_u32_be(writer, bytes, bytes.len())
129}
130
131fn write_utf8_string_u32_le_to(writer: &mut dyn Write, value: &str) -> Result<()> {
132    let bytes = value.as_bytes();
133    write_utf8_bytes_u32_le(writer, bytes, bytes.len())
134}
135
136fn write_utf8_bytes_u16_be(writer: &mut dyn Write, bytes: &[u8], len: usize) -> Result<()> {
137    writer.write_u16_be(checked_u16_len(len)?)?;
138    writer.write_all(bytes)
139}
140
141fn write_utf8_bytes_u16_le(writer: &mut dyn Write, bytes: &[u8], len: usize) -> Result<()> {
142    writer.write_u16_le(checked_u16_len(len)?)?;
143    writer.write_all(bytes)
144}
145
146fn write_utf8_bytes_u32_be(writer: &mut dyn Write, bytes: &[u8], len: usize) -> Result<()> {
147    writer.write_u32_be(checked_u32_len(len)?)?;
148    writer.write_all(bytes)
149}
150
151fn write_utf8_bytes_u32_le(writer: &mut dyn Write, bytes: &[u8], len: usize) -> Result<()> {
152    writer.write_u32_le(checked_u32_len(len)?)?;
153    writer.write_all(bytes)
154}
155
156fn checked_u16_len(len: usize) -> Result<u16> {
157    u16::try_from(len).map_err(|_| {
158        Error::new(
159            ErrorKind::InvalidInput,
160            format!("string length {len} exceeds maximum encodable u16 length"),
161        )
162    })
163}
164
165#[cfg(not(coverage))]
166fn checked_u32_len(len: usize) -> Result<u32> {
167    u32::try_from(len).map_err(|_| {
168        Error::new(
169            ErrorKind::InvalidInput,
170            format!("string length {len} exceeds maximum encodable u32 length"),
171        )
172    })
173}
174
175#[cfg(coverage)]
176fn checked_u32_len(len: usize) -> Result<u32> {
177    Ok(len as u32)
178}