1use byteorder::{BigEndian, ByteOrder};
2use serde::Serialize;
3
4use crate::innodb::constants::*;
5use crate::innodb::page_types::PageType;
6
7#[derive(Debug, Clone, Serialize)]
9pub struct FilHeader {
10 pub checksum: u32,
12 pub page_number: u32,
14 pub prev_page: u32,
17 pub next_page: u32,
20 pub lsn: u64,
22 pub page_type: PageType,
24 pub flush_lsn: u64,
26 pub space_id: u32,
28}
29
30impl FilHeader {
31 pub fn parse(data: &[u8]) -> Option<Self> {
35 if data.len() < SIZE_FIL_HEAD {
36 return None;
37 }
38
39 Some(FilHeader {
40 checksum: BigEndian::read_u32(&data[FIL_PAGE_SPACE_OR_CHKSUM..]),
41 page_number: BigEndian::read_u32(&data[FIL_PAGE_OFFSET..]),
42 prev_page: BigEndian::read_u32(&data[FIL_PAGE_PREV..]),
43 next_page: BigEndian::read_u32(&data[FIL_PAGE_NEXT..]),
44 lsn: BigEndian::read_u64(&data[FIL_PAGE_LSN..]),
45 page_type: PageType::from_u16(BigEndian::read_u16(&data[FIL_PAGE_TYPE..])),
46 flush_lsn: BigEndian::read_u64(&data[FIL_PAGE_FILE_FLUSH_LSN..]),
47 space_id: BigEndian::read_u32(&data[FIL_PAGE_SPACE_ID..]),
48 })
49 }
50
51 pub fn has_prev(&self) -> bool {
53 self.prev_page != FIL_NULL && self.prev_page != 0
54 }
55
56 pub fn has_next(&self) -> bool {
58 self.next_page != FIL_NULL && self.next_page != 0
59 }
60}
61
62#[derive(Debug, Clone, Serialize)]
64pub struct FilTrailer {
65 pub checksum: u32,
67 pub lsn_low32: u32,
69}
70
71impl FilTrailer {
72 pub fn parse(data: &[u8]) -> Option<Self> {
77 if data.len() < SIZE_FIL_TRAILER {
78 return None;
79 }
80
81 Some(FilTrailer {
82 checksum: BigEndian::read_u32(&data[0..]),
83 lsn_low32: BigEndian::read_u32(&data[4..]),
84 })
85 }
86}
87
88#[derive(Debug, Clone, Serialize)]
90pub struct FspHeader {
91 pub space_id: u32,
93 pub size: u32,
95 pub free_limit: u32,
97 pub flags: u32,
99 pub frag_n_used: u32,
101}
102
103impl FspHeader {
104 pub fn parse(page_data: &[u8]) -> Option<Self> {
108 let offset = FIL_PAGE_DATA;
109 if page_data.len() < offset + FSP_HEADER_SIZE {
110 return None;
111 }
112 let data = &page_data[offset..];
113
114 Some(FspHeader {
115 space_id: BigEndian::read_u32(&data[FSP_SPACE_ID..]),
116 size: BigEndian::read_u32(&data[FSP_SIZE..]),
117 free_limit: BigEndian::read_u32(&data[FSP_FREE_LIMIT..]),
118 flags: BigEndian::read_u32(&data[FSP_SPACE_FLAGS..]),
119 frag_n_used: BigEndian::read_u32(&data[FSP_FRAG_N_USED..]),
120 })
121 }
122
123 pub fn page_size_from_flags(&self) -> u32 {
127 let ssize = (self.flags & FSP_FLAGS_MASK_PAGE_SSIZE) >> FSP_FLAGS_POS_PAGE_SSIZE;
128 if ssize == 0 {
129 SIZE_PAGE_DEFAULT
131 } else {
132 1u32 << (ssize + 9)
139 }
140 }
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146
147 fn make_fil_header_bytes(
148 checksum: u32,
149 page_num: u32,
150 prev: u32,
151 next: u32,
152 lsn: u64,
153 page_type: u16,
154 flush_lsn: u64,
155 space_id: u32,
156 ) -> Vec<u8> {
157 let mut buf = vec![0u8; SIZE_FIL_HEAD];
158 BigEndian::write_u32(&mut buf[FIL_PAGE_SPACE_OR_CHKSUM..], checksum);
159 BigEndian::write_u32(&mut buf[FIL_PAGE_OFFSET..], page_num);
160 BigEndian::write_u32(&mut buf[FIL_PAGE_PREV..], prev);
161 BigEndian::write_u32(&mut buf[FIL_PAGE_NEXT..], next);
162 BigEndian::write_u64(&mut buf[FIL_PAGE_LSN..], lsn);
163 BigEndian::write_u16(&mut buf[FIL_PAGE_TYPE..], page_type);
164 BigEndian::write_u64(&mut buf[FIL_PAGE_FILE_FLUSH_LSN..], flush_lsn);
165 BigEndian::write_u32(&mut buf[FIL_PAGE_SPACE_ID..], space_id);
166 buf
167 }
168
169 #[test]
170 fn test_fil_header_parse() {
171 let data = make_fil_header_bytes(
172 0x12345678, 42, 41, 43, 1000, 17855, 2000, 5, );
181 let hdr = FilHeader::parse(&data).unwrap();
182 assert_eq!(hdr.checksum, 0x12345678);
183 assert_eq!(hdr.page_number, 42);
184 assert_eq!(hdr.prev_page, 41);
185 assert_eq!(hdr.next_page, 43);
186 assert_eq!(hdr.lsn, 1000);
187 assert_eq!(hdr.page_type, PageType::Index);
188 assert_eq!(hdr.flush_lsn, 2000);
189 assert_eq!(hdr.space_id, 5);
190 assert!(hdr.has_prev());
191 assert!(hdr.has_next());
192 }
193
194 #[test]
195 fn test_fil_header_null_pages() {
196 let data = make_fil_header_bytes(0, 0, FIL_NULL, FIL_NULL, 0, 0, 0, 0);
197 let hdr = FilHeader::parse(&data).unwrap();
198 assert!(!hdr.has_prev());
199 assert!(!hdr.has_next());
200 }
201
202 #[test]
203 fn test_fil_header_too_short() {
204 let data = vec![0u8; 10];
205 assert!(FilHeader::parse(&data).is_none());
206 }
207
208 #[test]
209 fn test_fil_trailer_parse() {
210 let mut data = vec![0u8; 8];
211 BigEndian::write_u32(&mut data[0..], 0xAABBCCDD);
212 BigEndian::write_u32(&mut data[4..], 0x11223344);
213 let trl = FilTrailer::parse(&data).unwrap();
214 assert_eq!(trl.checksum, 0xAABBCCDD);
215 assert_eq!(trl.lsn_low32, 0x11223344);
216 }
217
218 #[test]
219 fn test_fsp_header_page_size() {
220 let fsp = FspHeader {
221 space_id: 0,
222 size: 100,
223 free_limit: 64,
224 flags: 0, frag_n_used: 0,
226 };
227 assert_eq!(fsp.page_size_from_flags(), SIZE_PAGE_DEFAULT);
228
229 let fsp_16k = FspHeader {
231 flags: 5 << FSP_FLAGS_POS_PAGE_SSIZE,
232 ..fsp
233 };
234 assert_eq!(fsp_16k.page_size_from_flags(), 16384);
235
236 let fsp_4k = FspHeader {
238 flags: 3 << FSP_FLAGS_POS_PAGE_SSIZE,
239 ..fsp
240 };
241 assert_eq!(fsp_4k.page_size_from_flags(), 4096);
242 }
243}