1use byteorder::{BigEndian, ByteOrder};
13use serde::Serialize;
14
15use crate::innodb::constants::*;
16use crate::innodb::page_types::PageType;
17
18#[derive(Debug, Clone, Serialize)]
20pub struct FilHeader {
21 pub checksum: u32,
23 pub page_number: u32,
25 pub prev_page: u32,
28 pub next_page: u32,
31 pub lsn: u64,
33 pub page_type: PageType,
35 pub flush_lsn: u64,
37 pub space_id: u32,
39}
40
41impl FilHeader {
42 pub fn parse(data: &[u8]) -> Option<Self> {
46 if data.len() < SIZE_FIL_HEAD {
47 return None;
48 }
49
50 Some(FilHeader {
51 checksum: BigEndian::read_u32(&data[FIL_PAGE_SPACE_OR_CHKSUM..]),
52 page_number: BigEndian::read_u32(&data[FIL_PAGE_OFFSET..]),
53 prev_page: BigEndian::read_u32(&data[FIL_PAGE_PREV..]),
54 next_page: BigEndian::read_u32(&data[FIL_PAGE_NEXT..]),
55 lsn: BigEndian::read_u64(&data[FIL_PAGE_LSN..]),
56 page_type: PageType::from_u16(BigEndian::read_u16(&data[FIL_PAGE_TYPE..])),
57 flush_lsn: BigEndian::read_u64(&data[FIL_PAGE_FILE_FLUSH_LSN..]),
58 space_id: BigEndian::read_u32(&data[FIL_PAGE_SPACE_ID..]),
59 })
60 }
61
62 pub fn has_prev(&self) -> bool {
64 self.prev_page != FIL_NULL && self.prev_page != 0
65 }
66
67 pub fn has_next(&self) -> bool {
69 self.next_page != FIL_NULL && self.next_page != 0
70 }
71}
72
73#[derive(Debug, Clone, Serialize)]
75pub struct FilTrailer {
76 pub checksum: u32,
78 pub lsn_low32: u32,
80}
81
82impl FilTrailer {
83 pub fn parse(data: &[u8]) -> Option<Self> {
88 if data.len() < SIZE_FIL_TRAILER {
89 return None;
90 }
91
92 Some(FilTrailer {
93 checksum: BigEndian::read_u32(&data[0..]),
94 lsn_low32: BigEndian::read_u32(&data[4..]),
95 })
96 }
97}
98
99#[derive(Debug, Clone, Serialize)]
101pub struct FspHeader {
102 pub space_id: u32,
104 pub size: u32,
106 pub free_limit: u32,
108 pub flags: u32,
110 pub frag_n_used: u32,
112}
113
114impl FspHeader {
115 pub fn parse(page_data: &[u8]) -> Option<Self> {
119 let offset = FIL_PAGE_DATA;
120 if page_data.len() < offset + FSP_HEADER_SIZE {
121 return None;
122 }
123 let data = &page_data[offset..];
124
125 Some(FspHeader {
126 space_id: BigEndian::read_u32(&data[FSP_SPACE_ID..]),
127 size: BigEndian::read_u32(&data[FSP_SIZE..]),
128 free_limit: BigEndian::read_u32(&data[FSP_FREE_LIMIT..]),
129 flags: BigEndian::read_u32(&data[FSP_SPACE_FLAGS..]),
130 frag_n_used: BigEndian::read_u32(&data[FSP_FRAG_N_USED..]),
131 })
132 }
133
134 pub fn page_size_from_flags(&self) -> u32 {
138 let ssize = (self.flags & FSP_FLAGS_MASK_PAGE_SSIZE) >> FSP_FLAGS_POS_PAGE_SSIZE;
139 if ssize == 0 {
140 SIZE_PAGE_DEFAULT
142 } else {
143 1u32 << (ssize + 9)
150 }
151 }
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157
158 fn make_fil_header_bytes(
159 checksum: u32,
160 page_num: u32,
161 prev: u32,
162 next: u32,
163 lsn: u64,
164 page_type: u16,
165 flush_lsn: u64,
166 space_id: u32,
167 ) -> Vec<u8> {
168 let mut buf = vec![0u8; SIZE_FIL_HEAD];
169 BigEndian::write_u32(&mut buf[FIL_PAGE_SPACE_OR_CHKSUM..], checksum);
170 BigEndian::write_u32(&mut buf[FIL_PAGE_OFFSET..], page_num);
171 BigEndian::write_u32(&mut buf[FIL_PAGE_PREV..], prev);
172 BigEndian::write_u32(&mut buf[FIL_PAGE_NEXT..], next);
173 BigEndian::write_u64(&mut buf[FIL_PAGE_LSN..], lsn);
174 BigEndian::write_u16(&mut buf[FIL_PAGE_TYPE..], page_type);
175 BigEndian::write_u64(&mut buf[FIL_PAGE_FILE_FLUSH_LSN..], flush_lsn);
176 BigEndian::write_u32(&mut buf[FIL_PAGE_SPACE_ID..], space_id);
177 buf
178 }
179
180 #[test]
181 fn test_fil_header_parse() {
182 let data = make_fil_header_bytes(
183 0x12345678, 42, 41, 43, 1000, 17855, 2000, 5, );
192 let hdr = FilHeader::parse(&data).unwrap();
193 assert_eq!(hdr.checksum, 0x12345678);
194 assert_eq!(hdr.page_number, 42);
195 assert_eq!(hdr.prev_page, 41);
196 assert_eq!(hdr.next_page, 43);
197 assert_eq!(hdr.lsn, 1000);
198 assert_eq!(hdr.page_type, PageType::Index);
199 assert_eq!(hdr.flush_lsn, 2000);
200 assert_eq!(hdr.space_id, 5);
201 assert!(hdr.has_prev());
202 assert!(hdr.has_next());
203 }
204
205 #[test]
206 fn test_fil_header_null_pages() {
207 let data = make_fil_header_bytes(0, 0, FIL_NULL, FIL_NULL, 0, 0, 0, 0);
208 let hdr = FilHeader::parse(&data).unwrap();
209 assert!(!hdr.has_prev());
210 assert!(!hdr.has_next());
211 }
212
213 #[test]
214 fn test_fil_header_too_short() {
215 let data = vec![0u8; 10];
216 assert!(FilHeader::parse(&data).is_none());
217 }
218
219 #[test]
220 fn test_fil_trailer_parse() {
221 let mut data = vec![0u8; 8];
222 BigEndian::write_u32(&mut data[0..], 0xAABBCCDD);
223 BigEndian::write_u32(&mut data[4..], 0x11223344);
224 let trl = FilTrailer::parse(&data).unwrap();
225 assert_eq!(trl.checksum, 0xAABBCCDD);
226 assert_eq!(trl.lsn_low32, 0x11223344);
227 }
228
229 #[test]
230 fn test_fsp_header_page_size() {
231 let fsp = FspHeader {
232 space_id: 0,
233 size: 100,
234 free_limit: 64,
235 flags: 0, frag_n_used: 0,
237 };
238 assert_eq!(fsp.page_size_from_flags(), SIZE_PAGE_DEFAULT);
239
240 let fsp_16k = FspHeader {
242 flags: 5 << FSP_FLAGS_POS_PAGE_SSIZE,
243 ..fsp
244 };
245 assert_eq!(fsp_16k.page_size_from_flags(), 16384);
246
247 let fsp_4k = FspHeader {
249 flags: 3 << FSP_FLAGS_POS_PAGE_SSIZE,
250 ..fsp
251 };
252 assert_eq!(fsp_4k.page_size_from_flags(), 4096);
253 }
254}