• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 //! package table module defines the package table file format and methods for serialization
18 //! and deserialization
19 
20 use crate::{
21     get_bucket_index, read_str_from_bytes, read_u32_from_bytes, read_u64_from_bytes,
22     read_u8_from_bytes,
23 };
24 use crate::{AconfigStorageError, StorageFileType};
25 use anyhow::anyhow;
26 use serde::{Deserialize, Serialize};
27 use std::fmt;
28 
29 /// Package table header struct
30 #[derive(PartialEq, Serialize, Deserialize)]
31 pub struct PackageTableHeader {
32     pub version: u32,
33     pub container: String,
34     pub file_type: u8,
35     pub file_size: u32,
36     pub num_packages: u32,
37     pub bucket_offset: u32,
38     pub node_offset: u32,
39 }
40 
41 /// Implement debug print trait for header
42 impl fmt::Debug for PackageTableHeader {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result43     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
44         writeln!(
45             f,
46             "Version: {}, Container: {}, File Type: {:?}, File Size: {}",
47             self.version,
48             self.container,
49             StorageFileType::try_from(self.file_type),
50             self.file_size
51         )?;
52         writeln!(
53             f,
54             "Num of Packages: {}, Bucket Offset:{}, Node Offset: {}",
55             self.num_packages, self.bucket_offset, self.node_offset
56         )?;
57         Ok(())
58     }
59 }
60 
61 impl PackageTableHeader {
62     /// Serialize to bytes
into_bytes(&self) -> Vec<u8>63     pub fn into_bytes(&self) -> Vec<u8> {
64         let mut result = Vec::new();
65         result.extend_from_slice(&self.version.to_le_bytes());
66         let container_bytes = self.container.as_bytes();
67         result.extend_from_slice(&(container_bytes.len() as u32).to_le_bytes());
68         result.extend_from_slice(container_bytes);
69         result.extend_from_slice(&self.file_type.to_le_bytes());
70         result.extend_from_slice(&self.file_size.to_le_bytes());
71         result.extend_from_slice(&self.num_packages.to_le_bytes());
72         result.extend_from_slice(&self.bucket_offset.to_le_bytes());
73         result.extend_from_slice(&self.node_offset.to_le_bytes());
74         result
75     }
76 
77     /// Deserialize from bytes
from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError>78     pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {
79         let mut head = 0;
80         let table = Self {
81             version: read_u32_from_bytes(bytes, &mut head)?,
82             container: read_str_from_bytes(bytes, &mut head)?,
83             file_type: read_u8_from_bytes(bytes, &mut head)?,
84             file_size: read_u32_from_bytes(bytes, &mut head)?,
85             num_packages: read_u32_from_bytes(bytes, &mut head)?,
86             bucket_offset: read_u32_from_bytes(bytes, &mut head)?,
87             node_offset: read_u32_from_bytes(bytes, &mut head)?,
88         };
89         if table.file_type != StorageFileType::PackageMap as u8 {
90             return Err(AconfigStorageError::BytesParseFail(anyhow!(
91                 "binary file is not a package map"
92             )));
93         }
94         Ok(table)
95     }
96 }
97 
98 /// Package table node struct
99 #[derive(PartialEq, Serialize, Deserialize)]
100 pub struct PackageTableNode {
101     pub package_name: String,
102     pub package_id: u32,
103     pub fingerprint: u64,
104     // The index of the first boolean flag in this aconfig package among all boolean
105     // flags in this container.
106     pub boolean_start_index: u32,
107     pub next_offset: Option<u32>,
108 }
109 
110 /// Implement debug print trait for node
111 impl fmt::Debug for PackageTableNode {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result112     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
113         writeln!(
114             f,
115             "Package: {}, Id: {}, Fingerprint: {}, Boolean flag start index: {}, Next: {:?}",
116             self.package_name,
117             self.package_id,
118             self.fingerprint,
119             self.boolean_start_index,
120             self.next_offset
121         )?;
122         Ok(())
123     }
124 }
125 
126 impl PackageTableNode {
127     /// Serialize to bytes
into_bytes(&self, version: u32) -> Vec<u8>128     pub fn into_bytes(&self, version: u32) -> Vec<u8> {
129         match version {
130             1 => Self::into_bytes_v1(self),
131             2 => Self::into_bytes_v2(self),
132             // TODO(b/316357686): into_bytes should return a Result.
133             _ => Self::into_bytes_v2(&self),
134         }
135     }
136 
into_bytes_v1(&self) -> Vec<u8>137     fn into_bytes_v1(&self) -> Vec<u8> {
138         let mut result = Vec::new();
139         let name_bytes = self.package_name.as_bytes();
140         result.extend_from_slice(&(name_bytes.len() as u32).to_le_bytes());
141         result.extend_from_slice(name_bytes);
142         result.extend_from_slice(&self.package_id.to_le_bytes());
143         result.extend_from_slice(&self.boolean_start_index.to_le_bytes());
144         result.extend_from_slice(&self.next_offset.unwrap_or(0).to_le_bytes());
145         result
146     }
147 
into_bytes_v2(&self) -> Vec<u8>148     fn into_bytes_v2(&self) -> Vec<u8> {
149         let mut result = Vec::new();
150         let name_bytes = self.package_name.as_bytes();
151         result.extend_from_slice(&(name_bytes.len() as u32).to_le_bytes());
152         result.extend_from_slice(name_bytes);
153         result.extend_from_slice(&self.package_id.to_le_bytes());
154         result.extend_from_slice(&self.fingerprint.to_le_bytes());
155         result.extend_from_slice(&self.boolean_start_index.to_le_bytes());
156         result.extend_from_slice(&self.next_offset.unwrap_or(0).to_le_bytes());
157         result
158     }
159 
160     /// Deserialize from bytes based on file version.
from_bytes(bytes: &[u8], version: u32) -> Result<Self, AconfigStorageError>161     pub fn from_bytes(bytes: &[u8], version: u32) -> Result<Self, AconfigStorageError> {
162         match version {
163             1 => Self::from_bytes_v1(bytes),
164             2 => Self::from_bytes_v2(bytes),
165             _ => {
166                 return Err(AconfigStorageError::BytesParseFail(anyhow!(
167                     "Binary file is an unsupported version: {}",
168                     version
169                 )))
170             }
171         }
172     }
173 
from_bytes_v1(bytes: &[u8]) -> Result<Self, AconfigStorageError>174     fn from_bytes_v1(bytes: &[u8]) -> Result<Self, AconfigStorageError> {
175         let mut head = 0;
176         let package_name = read_str_from_bytes(bytes, &mut head)?;
177         let package_id = read_u32_from_bytes(bytes, &mut head)?;
178         // v1 does not have fingerprint, so just set to 0.
179         let fingerprint: u64 = 0;
180         let boolean_start_index = read_u32_from_bytes(bytes, &mut head)?;
181         let next_offset = match read_u32_from_bytes(bytes, &mut head)? {
182             0 => None,
183             val => Some(val),
184         };
185 
186         let node = Self { package_name, package_id, fingerprint, boolean_start_index, next_offset };
187         Ok(node)
188     }
189 
from_bytes_v2(bytes: &[u8]) -> Result<Self, AconfigStorageError>190     fn from_bytes_v2(bytes: &[u8]) -> Result<Self, AconfigStorageError> {
191         let mut head = 0;
192         let package_name = read_str_from_bytes(bytes, &mut head)?;
193         let package_id = read_u32_from_bytes(bytes, &mut head)?;
194         let fingerprint = read_u64_from_bytes(bytes, &mut head)?;
195         let boolean_start_index = read_u32_from_bytes(bytes, &mut head)?;
196         let next_offset = match read_u32_from_bytes(bytes, &mut head)? {
197             0 => None,
198             val => Some(val),
199         };
200 
201         let node = Self { package_name, package_id, fingerprint, boolean_start_index, next_offset };
202         Ok(node)
203     }
204 
205     /// Get the bucket index for a package table node, defined it here so the
206     /// construction side (aconfig binary) and consumption side (flag read lib)
207     /// use the same method of hashing
find_bucket_index(package: &str, num_buckets: u32) -> u32208     pub fn find_bucket_index(package: &str, num_buckets: u32) -> u32 {
209         get_bucket_index(package.as_bytes(), num_buckets)
210     }
211 }
212 
213 /// Package table struct
214 #[derive(PartialEq, Serialize, Deserialize)]
215 pub struct PackageTable {
216     pub header: PackageTableHeader,
217     pub buckets: Vec<Option<u32>>,
218     pub nodes: Vec<PackageTableNode>,
219 }
220 
221 /// Implement debug print trait for package table
222 impl fmt::Debug for PackageTable {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result223     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
224         writeln!(f, "Header:")?;
225         write!(f, "{:?}", self.header)?;
226         writeln!(f, "Buckets:")?;
227         writeln!(f, "{:?}", self.buckets)?;
228         writeln!(f, "Nodes:")?;
229         for node in self.nodes.iter() {
230             write!(f, "{:?}", node)?;
231         }
232         Ok(())
233     }
234 }
235 
236 impl PackageTable {
237     /// Serialize to bytes
into_bytes(&self) -> Vec<u8>238     pub fn into_bytes(&self) -> Vec<u8> {
239         [
240             self.header.into_bytes(),
241             self.buckets.iter().map(|v| v.unwrap_or(0).to_le_bytes()).collect::<Vec<_>>().concat(),
242             self.nodes
243                 .iter()
244                 .map(|v| v.into_bytes(self.header.version))
245                 .collect::<Vec<_>>()
246                 .concat(),
247         ]
248         .concat()
249     }
250 
251     /// Deserialize from bytes
from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError>252     pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {
253         let header = PackageTableHeader::from_bytes(bytes)?;
254         let num_packages = header.num_packages;
255         let num_buckets = crate::get_table_size(num_packages)?;
256         let mut head = header.into_bytes().len();
257         let buckets = (0..num_buckets)
258             .map(|_| match read_u32_from_bytes(bytes, &mut head).unwrap() {
259                 0 => None,
260                 val => Some(val),
261             })
262             .collect();
263         let nodes = (0..num_packages)
264             .map(|_| {
265                 let node = PackageTableNode::from_bytes(&bytes[head..], header.version)?;
266                 head += node.into_bytes(header.version).len();
267                 Ok(node)
268             })
269             .collect::<Result<Vec<_>, AconfigStorageError>>()
270             .map_err(|errmsg| {
271                 AconfigStorageError::BytesParseFail(anyhow!(
272                     "fail to parse package table: {}",
273                     errmsg
274                 ))
275             })?;
276 
277         let table = Self { header, buckets, nodes };
278         Ok(table)
279     }
280 }
281 
282 #[cfg(test)]
283 mod tests {
284     use super::*;
285     use crate::test_utils::create_test_package_table;
286     use crate::{read_u32_from_start_of_bytes, DEFAULT_FILE_VERSION, MAX_SUPPORTED_FILE_VERSION};
287 
288     #[test]
289     // this test point locks down the table serialization
test_serialization()290     fn test_serialization() {
291         for file_version in 1..=MAX_SUPPORTED_FILE_VERSION {
292             let package_table = create_test_package_table(file_version);
293             let header: &PackageTableHeader = &package_table.header;
294             let reinterpreted_header = PackageTableHeader::from_bytes(&header.into_bytes());
295             assert!(reinterpreted_header.is_ok());
296             assert_eq!(header, &reinterpreted_header.unwrap());
297 
298             let nodes: &Vec<PackageTableNode> = &package_table.nodes;
299             for node in nodes.iter() {
300                 let reinterpreted_node =
301                     PackageTableNode::from_bytes(&node.into_bytes(header.version), header.version)
302                         .unwrap();
303                 assert_eq!(node, &reinterpreted_node);
304             }
305 
306             let package_table_bytes = package_table.into_bytes();
307             let reinterpreted_table = PackageTable::from_bytes(&package_table_bytes);
308             assert!(reinterpreted_table.is_ok());
309             assert_eq!(&package_table, &reinterpreted_table.unwrap());
310             assert_eq!(package_table_bytes.len() as u32, header.file_size);
311         }
312     }
313 
314     #[test]
315     // this test point locks down that version number should be at the top of serialized
316     // bytes
test_version_number()317     fn test_version_number() {
318         let package_table = create_test_package_table(DEFAULT_FILE_VERSION);
319         let bytes = &package_table.into_bytes();
320         let unpacked_version = read_u32_from_start_of_bytes(bytes).unwrap();
321         assert_eq!(unpacked_version, DEFAULT_FILE_VERSION);
322     }
323 
324     #[test]
test_round_trip_default()325     fn test_round_trip_default() {
326         let table: PackageTable = create_test_package_table(DEFAULT_FILE_VERSION);
327         let table_bytes = table.into_bytes();
328 
329         let reinterpreted_table = PackageTable::from_bytes(&table_bytes).unwrap();
330 
331         assert_eq!(table, reinterpreted_table);
332     }
333 
334     #[test]
test_round_trip_max()335     fn test_round_trip_max() {
336         let table: PackageTable = create_test_package_table(MAX_SUPPORTED_FILE_VERSION);
337         let table_bytes = table.into_bytes();
338 
339         let reinterpreted_table = PackageTable::from_bytes(&table_bytes).unwrap();
340 
341         assert_eq!(table, reinterpreted_table);
342     }
343 
344     #[test]
345     // this test point locks down file type check
test_file_type_check()346     fn test_file_type_check() {
347         let mut package_table = create_test_package_table(DEFAULT_FILE_VERSION);
348         package_table.header.file_type = 123u8;
349         let error = PackageTable::from_bytes(&package_table.into_bytes()).unwrap_err();
350         assert_eq!(
351             format!("{:?}", error),
352             format!("BytesParseFail(binary file is not a package map)")
353         );
354     }
355 }
356