fsqlite_types/
encoding.rs1#[inline]
7#[must_use]
8pub fn read_u16_be(src: &[u8]) -> Option<u16> {
9 Some(u16::from_be_bytes(src.get(..2)?.try_into().ok()?))
10}
11
12#[inline]
13#[must_use]
14pub fn read_u32_be(src: &[u8]) -> Option<u32> {
15 Some(u32::from_be_bytes(src.get(..4)?.try_into().ok()?))
16}
17
18#[inline]
19#[must_use]
20pub fn read_i32_be(src: &[u8]) -> Option<i32> {
21 Some(i32::from_be_bytes(src.get(..4)?.try_into().ok()?))
22}
23
24#[inline]
25#[must_use]
26pub fn read_u32_le(src: &[u8]) -> Option<u32> {
27 Some(u32::from_le_bytes(src.get(..4)?.try_into().ok()?))
28}
29
30#[inline]
31#[must_use]
32pub fn read_u16_le(src: &[u8]) -> Option<u16> {
33 Some(u16::from_le_bytes(src.get(..2)?.try_into().ok()?))
34}
35
36#[inline]
37#[must_use]
38pub fn read_u64_le(src: &[u8]) -> Option<u64> {
39 Some(u64::from_le_bytes(src.get(..8)?.try_into().ok()?))
40}
41
42#[inline]
43pub fn write_u16_be(dst: &mut [u8], value: u16) -> Option<()> {
44 dst.get_mut(..2)?.copy_from_slice(&value.to_be_bytes());
45 Some(())
46}
47
48#[inline]
49pub fn write_u32_be(dst: &mut [u8], value: u32) -> Option<()> {
50 dst.get_mut(..4)?.copy_from_slice(&value.to_be_bytes());
51 Some(())
52}
53
54#[inline]
55pub fn write_i32_be(dst: &mut [u8], value: i32) -> Option<()> {
56 dst.get_mut(..4)?.copy_from_slice(&value.to_be_bytes());
57 Some(())
58}
59
60#[inline]
61pub fn write_u32_le(dst: &mut [u8], value: u32) -> Option<()> {
62 dst.get_mut(..4)?.copy_from_slice(&value.to_le_bytes());
63 Some(())
64}
65
66#[inline]
67pub fn write_u16_le(dst: &mut [u8], value: u16) -> Option<()> {
68 dst.get_mut(..2)?.copy_from_slice(&value.to_le_bytes());
69 Some(())
70}
71
72#[inline]
73pub fn write_u64_le(dst: &mut [u8], value: u64) -> Option<()> {
74 dst.get_mut(..8)?.copy_from_slice(&value.to_le_bytes());
75 Some(())
76}
77
78#[inline]
79#[must_use]
80pub fn read_u64_be(src: &[u8]) -> Option<u64> {
81 Some(u64::from_be_bytes(src.get(..8)?.try_into().ok()?))
82}
83
84#[inline]
85pub fn write_u64_be(dst: &mut [u8], value: u64) -> Option<()> {
86 dst.get_mut(..8)?.copy_from_slice(&value.to_be_bytes());
87 Some(())
88}
89
90#[inline]
91pub fn append_u16_be(buf: &mut Vec<u8>, value: u16) {
92 buf.extend_from_slice(&value.to_be_bytes());
93}
94
95#[inline]
96pub fn append_u32_be(buf: &mut Vec<u8>, value: u32) {
97 buf.extend_from_slice(&value.to_be_bytes());
98}
99
100#[inline]
101pub fn append_u64_be(buf: &mut Vec<u8>, value: u64) {
102 buf.extend_from_slice(&value.to_be_bytes());
103}
104
105#[inline]
106pub fn append_u32_le(buf: &mut Vec<u8>, value: u32) {
107 let mut scratch = [0u8; 4];
108 write_u32_le(&mut scratch, value).expect("fixed scratch width");
109 buf.extend_from_slice(&scratch);
110}
111
112#[inline]
113pub fn append_u16_le(buf: &mut Vec<u8>, value: u16) {
114 let mut scratch = [0u8; 2];
115 write_u16_le(&mut scratch, value).expect("fixed scratch width");
116 buf.extend_from_slice(&scratch);
117}
118
119#[inline]
120pub fn append_u64_le(buf: &mut Vec<u8>, value: u64) {
121 let mut scratch = [0u8; 8];
122 write_u64_le(&mut scratch, value).expect("fixed scratch width");
123 buf.extend_from_slice(&scratch);
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129 use crate::ecs::{PatchKind, VersionPointer};
130 use crate::{DatabaseHeader, ObjectId};
131
132 #[test]
133 fn test_sqlite_structures_big_endian() {
134 let header = DatabaseHeader {
135 change_counter: 0x0102_0304,
136 page_count: 0x1112_1314,
137 page_size: crate::PageSize::new(4096).expect("valid page size"),
138 ..DatabaseHeader::default()
139 };
140 let bytes = header.to_bytes().expect("header encodes");
141
142 assert_eq!(read_u16_be(&bytes[16..18]), Some(4096));
143 assert_eq!(read_u32_be(&bytes[24..28]), Some(0x0102_0304));
144 assert_eq!(read_u32_be(&bytes[28..32]), Some(0x1112_1314));
145 }
146
147 #[test]
148 fn test_native_ecs_structures_little_endian() {
149 let pointer = VersionPointer {
150 commit_seq: 0x0102_0304_0506_0708,
151 patch_object: ObjectId::from_bytes([7u8; 16]),
152 patch_kind: PatchKind::FullImage,
153 base_hint: None,
154 };
155 let bytes = pointer.to_bytes();
156 assert_eq!(
157 read_u64_le(&bytes[..8]),
158 Some(0x0102_0304_0506_0708),
159 "version pointer commit_seq must be little-endian"
160 );
161 }
162
163 #[test]
164 fn test_canonical_encoding_unique() {
165 let header = DatabaseHeader {
166 change_counter: 42,
167 page_count: 7,
168 ..DatabaseHeader::default()
169 };
170 let a = header.to_bytes().expect("encodes");
171 let b = header.to_bytes().expect("encodes");
172 assert_eq!(a, b, "same sqlite header must encode identically");
173
174 let pointer = VersionPointer {
175 commit_seq: 99,
176 patch_object: ObjectId::from_bytes([3u8; 16]),
177 patch_kind: PatchKind::SparseXor,
178 base_hint: Some(ObjectId::from_bytes([4u8; 16])),
179 };
180 assert_eq!(pointer.to_bytes(), pointer.to_bytes());
181 }
182
183 #[test]
184 fn test_roundtrip_encode_decode() {
185 let header = DatabaseHeader {
186 change_counter: 123,
187 page_count: 456,
188 ..DatabaseHeader::default()
189 };
190 let header_bytes = header.to_bytes().expect("encodes");
191 let parsed = DatabaseHeader::from_bytes(&header_bytes).expect("decodes");
192 assert_eq!(parsed, header);
193
194 let pointer = VersionPointer {
195 commit_seq: 777,
196 patch_object: ObjectId::from_bytes([9u8; 16]),
197 patch_kind: PatchKind::FullImage,
198 base_hint: Some(ObjectId::from_bytes([10u8; 16])),
199 };
200 let bytes = pointer.to_bytes();
201 let decoded = VersionPointer::from_bytes(&bytes).expect("pointer decodes");
202 assert_eq!(decoded, pointer);
203 }
204
205 #[test]
206 fn test_mixed_endian_udp_documented() {
207 let mut packet = [0u8; 10];
210 write_u16_be(&mut packet[0..2], 9443).expect("u16 be");
211 write_u16_be(&mut packet[2..4], 9444).expect("u16 be");
212 write_u32_le(&mut packet[4..8], 128).expect("u32 le");
213 write_u16_be(&mut packet[8..10], 1).expect("u16 be");
214
215 assert_eq!(read_u16_be(&packet[0..2]), Some(9443));
216 assert_eq!(read_u16_be(&packet[2..4]), Some(9444));
217 assert_eq!(read_u32_le(&packet[4..8]), Some(128));
218 assert_eq!(read_u16_be(&packet[8..10]), Some(1));
219 }
220}