1use crate::error::{Result, SqliteError};
17use crate::header::TextEncoding;
18use crate::varint;
19
20#[derive(Debug, Clone, PartialEq)]
29pub enum SqliteValue {
30 Null,
31 Integer(i64),
32 Real(f64),
33 Text(String),
34 Blob(Vec<u8>),
35}
36
37impl SqliteValue {
38 pub fn type_name(&self) -> &'static str {
41 match self {
42 SqliteValue::Null => "null",
43 SqliteValue::Integer(_) => "integer",
44 SqliteValue::Real(_) => "real",
45 SqliteValue::Text(_) => "text",
46 SqliteValue::Blob(_) => "blob",
47 }
48 }
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52enum SerialType {
53 Null,
54 Int(u8),
55 Float,
56 Zero,
58 One,
60 Blob(usize),
61 Text(usize),
62}
63
64impl SerialType {
65 fn from_code(code: u64) -> Result<SerialType> {
66 Ok(match code {
67 0 => SerialType::Null,
68 1 => SerialType::Int(1),
69 2 => SerialType::Int(2),
70 3 => SerialType::Int(3),
71 4 => SerialType::Int(4),
72 5 => SerialType::Int(6),
73 6 => SerialType::Int(8),
74 7 => SerialType::Float,
75 8 => SerialType::Zero,
76 9 => SerialType::One,
77 10 | 11 => {
80 return Err(SqliteError::malformed(
81 None,
82 format!("serial type {code} is reserved for internal use"),
83 ))
84 }
85 even if even % 2 == 0 => {
86 let len = (even - 12) / 2;
87 SerialType::Blob(usize::try_from(len).map_err(|_| {
88 SqliteError::malformed(None, format!("blob of {len} bytes is impossibly large"))
89 })?)
90 }
91 odd => {
92 let len = (odd - 13) / 2;
93 SerialType::Text(usize::try_from(len).map_err(|_| {
94 SqliteError::malformed(None, format!("text of {len} bytes is impossibly large"))
95 })?)
96 }
97 })
98 }
99
100 fn width(self) -> usize {
102 match self {
103 SerialType::Null | SerialType::Zero | SerialType::One => 0,
104 SerialType::Int(n) => n as usize,
105 SerialType::Float => 8,
106 SerialType::Blob(n) | SerialType::Text(n) => n,
107 }
108 }
109}
110
111pub fn decode(payload: &[u8], encoding: TextEncoding) -> Result<Vec<SqliteValue>> {
123 let (header_len, varint_len) = varint::read_unsigned(payload)?;
124 let header_len = usize::try_from(header_len)
125 .map_err(|_| SqliteError::malformed(None, "record header length does not fit in memory"))?;
126 if header_len < varint_len || header_len > payload.len() {
127 return Err(SqliteError::malformed(
128 None,
129 format!(
130 "record header claims {header_len} bytes of a {} byte payload",
131 payload.len()
132 ),
133 ));
134 }
135
136 let mut types = Vec::new();
138 let mut at = varint_len;
139 while at < header_len {
140 let (code, len) = varint::read_unsigned(&payload[at..header_len])?;
141 types.push(SerialType::from_code(code)?);
142 at += len;
143 }
144
145 let mut values = Vec::with_capacity(types.len());
147 let mut at = header_len;
148 for ty in types {
149 let width = ty.width();
150 let end = at.checked_add(width).ok_or_else(|| {
151 SqliteError::malformed(None, "a record value overflows the payload length")
152 })?;
153 if end > payload.len() {
154 return Err(SqliteError::malformed(
155 None,
156 format!(
157 "a {width} byte value at offset {at} runs past the {} byte payload",
158 payload.len()
159 ),
160 ));
161 }
162 let bytes = &payload[at..end];
163 values.push(match ty {
164 SerialType::Null => SqliteValue::Null,
165 SerialType::Zero => SqliteValue::Integer(0),
166 SerialType::One => SqliteValue::Integer(1),
167 SerialType::Int(_) => SqliteValue::Integer(signed_be(bytes)),
168 SerialType::Float => {
169 let mut eight = [0u8; 8];
170 eight.copy_from_slice(bytes);
171 SqliteValue::Real(f64::from_bits(u64::from_be_bytes(eight)))
172 }
173 SerialType::Blob(_) => SqliteValue::Blob(bytes.to_vec()),
174 SerialType::Text(_) => SqliteValue::Text(text_from(bytes, encoding)?),
175 });
176 at = end;
177 }
178
179 Ok(values)
180}
181
182fn text_from(bytes: &[u8], encoding: TextEncoding) -> Result<String> {
189 match encoding {
190 TextEncoding::Utf8 => match std::str::from_utf8(bytes) {
191 Ok(text) => Ok(text.to_string()),
192 Err(e) => Err(SqliteError::malformed(
193 None,
194 format!("a text value is not valid utf-8: {e}"),
195 )),
196 },
197 TextEncoding::Utf16Le | TextEncoding::Utf16Be => {
198 if !bytes.len().is_multiple_of(2) {
199 return Err(SqliteError::malformed(
200 None,
201 format!(
202 "a utf-16 text value is {} bytes long, which is not a whole number of \
203 code units",
204 bytes.len()
205 ),
206 ));
207 }
208 let big_endian = encoding == TextEncoding::Utf16Be;
209 let units: Vec<u16> = bytes
210 .as_chunks::<2>()
211 .0
212 .iter()
213 .map(|pair| {
214 if big_endian {
215 u16::from_be_bytes([pair[0], pair[1]])
216 } else {
217 u16::from_le_bytes([pair[0], pair[1]])
218 }
219 })
220 .collect();
221 String::from_utf16(&units).map_err(|_| {
222 SqliteError::malformed(
223 None,
224 "a utf-16 text value holds an unpaired surrogate, so it is not text",
225 )
226 })
227 }
228 }
229}
230
231fn signed_be(bytes: &[u8]) -> i64 {
233 let mut value: u64 = 0;
234 for byte in bytes {
235 value = (value << 8) | *byte as u64;
236 }
237 let bits = 8 * bytes.len() as u32;
238 if bits < 64 && (value >> (bits - 1)) & 1 == 1 {
239 (value | (!0u64 << bits)) as i64
241 } else {
242 value as i64
243 }
244}
245
246#[cfg(test)]
247mod tests {
248 use super::*;
249
250 #[test]
251 fn sign_extension_covers_every_width() {
252 assert_eq!(signed_be(&[0x7f]), 127);
253 assert_eq!(signed_be(&[0x80]), -128);
254 assert_eq!(signed_be(&[0xff]), -1);
255 assert_eq!(signed_be(&[0x80, 0x00]), -32768);
256 assert_eq!(signed_be(&[0x80, 0x00, 0x00]), -8388608);
257 assert_eq!(signed_be(&[0x7f, 0xff, 0xff]), 8388607);
258 assert_eq!(signed_be(&[0x80, 0, 0, 0]), -2147483648);
259 assert_eq!(signed_be(&[0x80, 0, 0, 0, 0, 0]), -140737488355328);
260 assert_eq!(
261 signed_be(&[0x7f, 0xff, 0xff, 0xff, 0xff, 0xff]),
262 140737488355327
263 );
264 assert_eq!(signed_be(&[0x80, 0, 0, 0, 0, 0, 0, 0]), i64::MIN);
265 assert_eq!(signed_be(&[0xff; 8]), -1);
266 }
267
268 #[test]
269 fn a_record_of_every_cheap_type() {
270 let payload = [6u8, 0, 8, 9, 1, 17, 0x2a, b'h', b'i'];
273 let values = decode(&payload, TextEncoding::Utf8).unwrap();
274 assert_eq!(
275 values,
276 vec![
277 SqliteValue::Null,
278 SqliteValue::Integer(0),
279 SqliteValue::Integer(1),
280 SqliteValue::Integer(42),
281 SqliteValue::Text("hi".to_string()),
282 ]
283 );
284 }
285
286 #[test]
287 fn an_empty_record_has_no_values() {
288 assert_eq!(decode(&[1u8], TextEncoding::Utf8).unwrap(), vec![]);
289 }
290
291 #[test]
292 fn reserved_serial_types_are_refused() {
293 assert!(decode(&[2u8, 10], TextEncoding::Utf8).is_err());
294 assert!(decode(&[2u8, 11], TextEncoding::Utf8).is_err());
295 }
296
297 #[test]
298 fn a_header_longer_than_the_payload_is_refused() {
299 assert!(decode(&[99u8, 0], TextEncoding::Utf8).is_err());
300 assert!(decode(&[], TextEncoding::Utf8).is_err());
301 }
302
303 #[test]
304 fn a_value_running_past_the_payload_is_refused() {
305 assert!(decode(&[2u8, 21, b'a'], TextEncoding::Utf8).is_err());
307 }
308
309 #[test]
310 fn invalid_utf8_in_a_text_value_is_refused() {
311 assert!(decode(&[2u8, 15, 0xff, 0xfe], TextEncoding::Utf8).is_err());
312 }
313
314 #[test]
315 fn floats_round_trip_bit_for_bit() {
316 let mut payload = vec![2u8, 7];
317 payload.extend_from_slice(&(-2.25f64).to_be_bytes());
318 assert_eq!(
319 decode(&payload, TextEncoding::Utf8).unwrap(),
320 vec![SqliteValue::Real(-2.25)]
321 );
322 }
323}