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 use anyhow::Result;
18
19 use aconfig_storage_file::{
20 get_table_size, PackageTable, PackageTableHeader, PackageTableNode, StorageFileType,
21 FILE_VERSION,
22 };
23
24 use crate::storage::FlagPackage;
25
new_header(container: &str, num_packages: u32) -> PackageTableHeader26 fn new_header(container: &str, num_packages: u32) -> PackageTableHeader {
27 PackageTableHeader {
28 version: FILE_VERSION,
29 container: String::from(container),
30 file_type: StorageFileType::PackageMap as u8,
31 file_size: 0,
32 num_packages,
33 bucket_offset: 0,
34 node_offset: 0,
35 }
36 }
37
38 // a struct that contains PackageTableNode and a bunch of other information to help
39 // package table creation
40 #[derive(PartialEq, Debug)]
41 struct PackageTableNodeWrapper {
42 pub node: PackageTableNode,
43 pub bucket_index: u32,
44 }
45
46 impl PackageTableNodeWrapper {
new(package: &FlagPackage, num_buckets: u32) -> Self47 fn new(package: &FlagPackage, num_buckets: u32) -> Self {
48 let node = PackageTableNode {
49 package_name: String::from(package.package_name),
50 package_id: package.package_id,
51 boolean_start_index: package.boolean_start_index,
52 next_offset: None,
53 };
54 let bucket_index = PackageTableNode::find_bucket_index(package.package_name, num_buckets);
55 Self { node, bucket_index }
56 }
57 }
58
create_package_table(container: &str, packages: &[FlagPackage]) -> Result<PackageTable>59 pub fn create_package_table(container: &str, packages: &[FlagPackage]) -> Result<PackageTable> {
60 // create table
61 let num_packages = packages.len() as u32;
62 let num_buckets = get_table_size(num_packages)?;
63 let mut header = new_header(container, num_packages);
64 let mut buckets = vec![None; num_buckets as usize];
65 let mut node_wrappers: Vec<_> =
66 packages.iter().map(|pkg| PackageTableNodeWrapper::new(pkg, num_buckets)).collect();
67
68 // initialize all header fields
69 header.bucket_offset = header.into_bytes().len() as u32;
70 header.node_offset = header.bucket_offset + num_buckets * 4;
71 header.file_size = header.node_offset
72 + node_wrappers.iter().map(|x| x.node.into_bytes().len()).sum::<usize>() as u32;
73
74 // sort node_wrappers by bucket index for efficiency
75 node_wrappers.sort_by(|a, b| a.bucket_index.cmp(&b.bucket_index));
76
77 // fill all node offset
78 let mut offset = header.node_offset;
79 for i in 0..node_wrappers.len() {
80 let node_bucket_idx = node_wrappers[i].bucket_index;
81 let next_node_bucket_idx = if i + 1 < node_wrappers.len() {
82 Some(node_wrappers[i + 1].bucket_index)
83 } else {
84 None
85 };
86
87 if buckets[node_bucket_idx as usize].is_none() {
88 buckets[node_bucket_idx as usize] = Some(offset);
89 }
90 offset += node_wrappers[i].node.into_bytes().len() as u32;
91
92 if let Some(index) = next_node_bucket_idx {
93 if index == node_bucket_idx {
94 node_wrappers[i].node.next_offset = Some(offset);
95 }
96 }
97 }
98
99 let table = PackageTable {
100 header,
101 buckets,
102 nodes: node_wrappers.into_iter().map(|nw| nw.node).collect(),
103 };
104 Ok(table)
105 }
106
107 #[cfg(test)]
108 mod tests {
109 use super::*;
110 use crate::storage::{group_flags_by_package, tests::parse_all_test_flags};
111
create_test_package_table_from_source() -> Result<PackageTable>112 pub fn create_test_package_table_from_source() -> Result<PackageTable> {
113 let caches = parse_all_test_flags();
114 let packages = group_flags_by_package(caches.iter());
115 create_package_table("mockup", &packages)
116 }
117
118 #[test]
119 // this test point locks down the table creation and each field
test_table_contents()120 fn test_table_contents() {
121 let package_table = create_test_package_table_from_source();
122 assert!(package_table.is_ok());
123 let expected_package_table = aconfig_storage_file::test_utils::create_test_package_table();
124 assert_eq!(package_table.unwrap(), expected_package_table);
125 }
126 }
127