reddb_file/serverless/
boot.rs1use super::*;
2
3#[derive(Debug, Clone, PartialEq, Eq)]
4pub struct ServerlessBootIndexEntry {
5 pub kind: ServerlessPackKind,
6 pub relative_path: PathBuf,
7 pub required_first: bool,
8}
9
10impl ServerlessBootIndexEntry {
11 pub fn new(
12 kind: ServerlessPackKind,
13 relative_path: impl Into<PathBuf>,
14 required_first: bool,
15 ) -> Self {
16 Self {
17 kind,
18 relative_path: relative_path.into(),
19 required_first,
20 }
21 }
22}
23
24#[derive(Debug, Clone, PartialEq, Eq)]
25pub struct ServerlessBootIndex {
26 pub generation: u64,
27 pub entries: Vec<ServerlessBootIndexEntry>,
28}
29
30impl ServerlessBootIndex {
31 pub fn from_plan(plan: &ServerlessFilePlan) -> Self {
32 let mut entries = Vec::new();
33 for path in plan.cold_start_order() {
34 entries.push(ServerlessBootIndexEntry::new(
35 kind_for_artifact_path(&path),
36 relative_to_generation_dir(&path),
37 true,
38 ));
39 }
40 for path in ServerlessBootPlan::cold(plan).lazy_after_open {
41 entries.push(ServerlessBootIndexEntry::new(
42 kind_for_artifact_path(&path),
43 relative_to_generation_dir(&path),
44 false,
45 ));
46 }
47 Self {
48 generation: plan.generation,
49 entries,
50 }
51 }
52
53 pub fn required_first(&self) -> Vec<PathBuf> {
54 self.entries
55 .iter()
56 .filter(|entry| entry.required_first)
57 .map(|entry| entry.relative_path.clone())
58 .collect()
59 }
60
61 pub fn lazy_after_open(&self) -> Vec<PathBuf> {
62 self.entries
63 .iter()
64 .filter(|entry| !entry.required_first)
65 .map(|entry| entry.relative_path.clone())
66 .collect()
67 }
68
69 pub fn write_to_path(&self, path: impl AsRef<Path>) -> RdbFileResult<()> {
70 write_bytes(path, &self.encode())
71 }
72
73 pub fn read_from_path(path: impl AsRef<Path>) -> RdbFileResult<Self> {
74 Self::decode(&fs::read(path)?)
75 }
76
77 pub fn encode(&self) -> Vec<u8> {
78 let mut out = Vec::new();
79 out.extend_from_slice(SERVERLESS_BOOT_INDEX_MAGIC);
80 put_u16(&mut out, SERVERLESS_ARTIFACT_VERSION);
81 put_u64(&mut out, self.generation);
82 put_u32(&mut out, self.entries.len() as u32);
83 for entry in &self.entries {
84 out.push(u8::from(entry.kind));
85 out.push(u8::from(entry.required_first));
86 put_string(&mut out, &entry.relative_path.to_string_lossy());
87 }
88 let checksum = crc32(&out);
89 put_u32(&mut out, checksum);
90 out
91 }
92
93 pub fn decode(bytes: &[u8]) -> RdbFileResult<Self> {
94 verify_checksum(bytes)?;
95 let mut cursor = 0usize;
96 expect_magic(bytes, &mut cursor, SERVERLESS_BOOT_INDEX_MAGIC)?;
97 let version = take_u16(bytes, &mut cursor)?;
98 if version != SERVERLESS_ARTIFACT_VERSION {
99 return Err(RdbFileError::InvalidOperation(format!(
100 "unsupported serverless boot-index version {version}"
101 )));
102 }
103 let generation = take_u64(bytes, &mut cursor)?;
104 let count = take_u32(bytes, &mut cursor)? as usize;
105 let mut entries = Vec::with_capacity(count);
106 for _ in 0..count {
107 let kind = ServerlessPackKind::try_from(take_u8(bytes, &mut cursor)?)?;
108 let required_first = take_u8(bytes, &mut cursor)? != 0;
109 let relative_path = PathBuf::from(take_string(bytes, &mut cursor)?);
110 entries.push(ServerlessBootIndexEntry {
111 kind,
112 relative_path,
113 required_first,
114 });
115 }
116 reject_trailing_bytes(bytes, cursor)?;
117 Ok(Self {
118 generation,
119 entries,
120 })
121 }
122}