zero_mysql/protocol/
primitive.rs

1use crate::nightly::{cold_path, unlikely};
2
3use crate::error::{Error, Result, eyre};
4use zerocopy::FromBytes;
5use zerocopy::byteorder::little_endian::{U16 as U16LE, U32 as U32LE, U64 as U64LE};
6
7/// Read 1-byte integer
8pub fn read_int_1(data: &[u8]) -> Result<(u8, &[u8])> {
9    if unlikely(data.is_empty()) {
10        return Err(Error::LibraryBug(eyre!("read_int_1: empty buffer")));
11    }
12    Ok((data[0], &data[1..]))
13}
14
15/// Read 2-byte little-endian integer
16pub fn read_int_2(data: &[u8]) -> Result<(u16, &[u8])> {
17    if unlikely(data.len() < 2) {
18        return Err(Error::LibraryBug(eyre!(
19            "read_int_2: buffer too short: {} < 2",
20            data.len()
21        )));
22    }
23    let value = U16LE::ref_from_bytes(&data[..2])?.get();
24    Ok((value, &data[2..]))
25}
26
27/// Read 3-byte little-endian integer
28pub fn read_int_3(data: &[u8]) -> Result<(u32, &[u8])> {
29    if unlikely(data.len() < 3) {
30        return Err(Error::LibraryBug(eyre!(
31            "read_int_3: buffer too short: {} < 3",
32            data.len()
33        )));
34    }
35    let value = u32::from_le_bytes([data[0], data[1], data[2], 0]);
36    Ok((value, &data[3..]))
37}
38
39/// Read 4-byte little-endian integer
40pub fn read_int_4(data: &[u8]) -> Result<(u32, &[u8])> {
41    if unlikely(data.len() < 4) {
42        return Err(Error::LibraryBug(eyre!(
43            "read_int_4: buffer too short: {} < 4",
44            data.len()
45        )));
46    }
47    let value = U32LE::ref_from_bytes(&data[..4])?.get();
48    Ok((value, &data[4..]))
49}
50
51/// Read 6-byte little-endian integer
52pub fn read_int_6(data: &[u8]) -> Result<(u64, &[u8])> {
53    if unlikely(data.len() < 6) {
54        return Err(Error::LibraryBug(eyre!(
55            "read_int_6: buffer too short: {} < 6",
56            data.len()
57        )));
58    }
59    let value = u64::from_le_bytes([data[0], data[1], data[2], data[3], data[4], data[5], 0, 0]);
60    Ok((value, &data[6..]))
61}
62
63/// Read 8-byte little-endian integer
64pub fn read_int_8(data: &[u8]) -> Result<(u64, &[u8])> {
65    if unlikely(data.len() < 8) {
66        return Err(Error::LibraryBug(eyre!(
67            "read_int_8: buffer too short: {} < 8",
68            data.len()
69        )));
70    }
71    let value = U64LE::ref_from_bytes(&data[..8])?.get();
72    Ok((value, &data[8..]))
73}
74
75/// Read length-encoded integer
76pub fn read_int_lenenc(data: &[u8]) -> Result<(u64, &[u8])> {
77    match data.first() {
78        Some(0xFC) => {
79            let (val, rest) = read_int_2(&data[1..])?;
80            Ok((val as u64, rest))
81        }
82        Some(0xFD) => {
83            let (val, rest) = read_int_3(&data[1..])?;
84            Ok((val as u64, rest))
85        }
86        Some(0xFE) => {
87            let (val, rest) = read_int_8(&data[1..])?;
88            Ok((val, rest))
89        }
90        Some(val) => Ok((*val as u64, &data[1..])),
91        None => {
92            cold_path();
93            Err(Error::LibraryBug(eyre!("read_int_lenenc: empty buffer")))
94        }
95    }
96}
97
98/// Read fixed-length string
99pub fn read_string_fix(data: &[u8], len: usize) -> Result<(&[u8], &[u8])> {
100    if unlikely(data.len() < len) {
101        return Err(Error::LibraryBug(eyre!(
102            "read_string_fix: buffer too short: {} < {}",
103            data.len(),
104            len
105        )));
106    }
107    Ok((&data[..len], &data[len..]))
108}
109
110/// Read null-terminated string
111/// TODO: use memchr
112pub fn read_string_null(data: &[u8]) -> Result<(&[u8], &[u8])> {
113    for (i, &byte) in data.iter().enumerate() {
114        if byte == 0 {
115            return Ok((&data[..i], &data[i + 1..]));
116        }
117    }
118    Err(Error::LibraryBug(eyre!(
119        "read_string_null: no null terminator found"
120    )))
121}
122
123/// Read length-encoded string
124pub fn read_string_lenenc(data: &[u8]) -> Result<(&[u8], &[u8])> {
125    let (len, rest) = read_int_lenenc(data)?;
126    read_string_fix(rest, len as usize)
127}
128
129/// Write 1-byte integer
130#[inline]
131pub fn write_int_1(out: &mut Vec<u8>, value: u8) {
132    out.push(value);
133}
134
135/// Write 2-byte little-endian integer
136#[inline]
137pub fn write_int_2(out: &mut Vec<u8>, value: u16) {
138    out.extend_from_slice(&value.to_le_bytes());
139}
140
141/// Write 3-byte little-endian integer
142#[inline]
143pub fn write_int_3(out: &mut Vec<u8>, value: u32) {
144    out.extend_from_slice(&value.to_le_bytes()[..3]);
145}
146
147/// Write 4-byte little-endian integer
148#[inline]
149pub fn write_int_4(out: &mut Vec<u8>, value: u32) {
150    out.extend_from_slice(&value.to_le_bytes());
151}
152
153/// Write 8-byte little-endian integer
154#[inline]
155pub fn write_int_8(out: &mut Vec<u8>, value: u64) {
156    out.extend_from_slice(&value.to_le_bytes());
157}
158
159/// Write length-encoded integer
160pub fn write_int_lenenc(out: &mut Vec<u8>, value: u64) {
161    if value < 251 {
162        out.push(value as u8);
163    } else if value < (1 << 16) {
164        out.push(0xfc);
165        write_int_2(out, value as u16);
166    } else if value < (1 << 24) {
167        out.push(0xfd);
168        write_int_3(out, value as u32);
169    } else {
170        out.push(0xfe);
171        write_int_8(out, value);
172    }
173}
174
175/// Write fixed-length bytes
176#[inline]
177pub fn write_bytes_fix(out: &mut Vec<u8>, data: &[u8]) {
178    out.extend_from_slice(data);
179}
180
181/// Write null-terminated string
182#[inline]
183pub fn write_string_null(out: &mut Vec<u8>, bytes: &[u8]) {
184    out.extend_from_slice(bytes);
185    out.push(0);
186}
187
188/// Write length-encoded string
189#[inline]
190pub fn write_string_lenenc(out: &mut Vec<u8>, s: &str) {
191    write_int_lenenc(out, s.len() as u64);
192    out.extend_from_slice(s.as_bytes());
193}
194
195/// Write length-encoded bytes
196#[inline]
197pub fn write_bytes_lenenc(out: &mut Vec<u8>, data: &[u8]) {
198    write_int_lenenc(out, data.len() as u64);
199    out.extend_from_slice(data);
200}