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 };
22
23 use crate::storage::FlagPackage;
24
new_header(container: &str, num_packages: u32, version: u32) -> PackageTableHeader25 fn new_header(container: &str, num_packages: u32, version: u32) -> PackageTableHeader {
26 PackageTableHeader {
27 version,
28 container: String::from(container),
29 file_type: StorageFileType::PackageMap as u8,
30 file_size: 0,
31 num_packages,
32 bucket_offset: 0,
33 node_offset: 0,
34 }
35 }
36
37 // a struct that contains PackageTableNode and a bunch of other information to help
38 // package table creation
39 #[derive(PartialEq, Debug)]
40 struct PackageTableNodeWrapper {
41 pub node: PackageTableNode,
42 pub bucket_index: u32,
43 }
44
45 impl PackageTableNodeWrapper {
new(package: &FlagPackage, num_buckets: u32) -> Self46 fn new(package: &FlagPackage, num_buckets: u32) -> Self {
47 let node = PackageTableNode {
48 package_name: String::from(package.package_name),
49 package_id: package.package_id,
50 fingerprint: package.fingerprint,
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], version: u32, ) -> Result<PackageTable>59 pub fn create_package_table(
60 container: &str,
61 packages: &[FlagPackage],
62 version: u32,
63 ) -> Result<PackageTable> {
64 // create table
65 let num_packages = packages.len() as u32;
66 let num_buckets = get_table_size(num_packages)?;
67 let mut header = new_header(container, num_packages, version);
68 let mut buckets = vec![None; num_buckets as usize];
69 let mut node_wrappers: Vec<_> = packages
70 .iter()
71 .map(|pkg: &FlagPackage<'_>| PackageTableNodeWrapper::new(pkg, num_buckets))
72 .collect();
73
74 // initialize all header fields
75 header.bucket_offset = header.into_bytes().len() as u32;
76 header.node_offset = header.bucket_offset + num_buckets * 4;
77 header.file_size = header.node_offset
78 + node_wrappers.iter().map(|x| x.node.into_bytes(version).len()).sum::<usize>() as u32;
79
80 // sort node_wrappers by bucket index for efficiency
81 node_wrappers.sort_by(|a, b| a.bucket_index.cmp(&b.bucket_index));
82
83 // fill all node offset
84 let mut offset = header.node_offset;
85 for i in 0..node_wrappers.len() {
86 let node_bucket_idx = node_wrappers[i].bucket_index;
87 let next_node_bucket_idx = if i + 1 < node_wrappers.len() {
88 Some(node_wrappers[i + 1].bucket_index)
89 } else {
90 None
91 };
92
93 if buckets[node_bucket_idx as usize].is_none() {
94 buckets[node_bucket_idx as usize] = Some(offset);
95 }
96 offset += node_wrappers[i].node.into_bytes(version).len() as u32;
97
98 if let Some(index) = next_node_bucket_idx {
99 if index == node_bucket_idx {
100 node_wrappers[i].node.next_offset = Some(offset);
101 }
102 }
103 }
104
105 let table = PackageTable {
106 header,
107 buckets,
108 nodes: node_wrappers.into_iter().map(|nw| nw.node).collect(),
109 };
110 Ok(table)
111 }
112
113 #[cfg(test)]
114 mod tests {
115 use aconfig_storage_file::{DEFAULT_FILE_VERSION, MAX_SUPPORTED_FILE_VERSION};
116
117 use super::*;
118 use crate::storage::{group_flags_by_package, tests::parse_all_test_flags};
119
create_test_package_table_from_source(version: u32) -> Result<PackageTable>120 pub fn create_test_package_table_from_source(version: u32) -> Result<PackageTable> {
121 let caches = parse_all_test_flags();
122 let packages = group_flags_by_package(caches.iter(), version);
123 create_package_table("mockup", &packages, version)
124 }
125
126 #[test]
127 // this test point locks down the table creation and each field
test_table_contents_default_version()128 fn test_table_contents_default_version() {
129 let package_table_result = create_test_package_table_from_source(DEFAULT_FILE_VERSION);
130 assert!(package_table_result.is_ok());
131 let package_table = package_table_result.unwrap();
132
133 let expected_package_table =
134 aconfig_storage_file::test_utils::create_test_package_table(DEFAULT_FILE_VERSION);
135
136 assert_eq!(package_table.header, expected_package_table.header);
137 assert_eq!(package_table.buckets, expected_package_table.buckets);
138 for (node, expected_node) in
139 package_table.nodes.iter().zip(expected_package_table.nodes.iter())
140 {
141 assert_eq!(node.package_name, expected_node.package_name);
142 assert_eq!(node.package_id, expected_node.package_id);
143 assert_eq!(node.boolean_start_index, expected_node.boolean_start_index);
144 assert_eq!(node.next_offset, expected_node.next_offset);
145 }
146 }
147
148 #[test]
149 // this test point locks down the table creation and each field
test_table_contents_max_version()150 fn test_table_contents_max_version() {
151 let package_table_result =
152 create_test_package_table_from_source(MAX_SUPPORTED_FILE_VERSION);
153 assert!(package_table_result.is_ok());
154 let package_table = package_table_result.unwrap();
155
156 let expected_package_table =
157 aconfig_storage_file::test_utils::create_test_package_table(MAX_SUPPORTED_FILE_VERSION);
158
159 assert_eq!(package_table.header, expected_package_table.header);
160 assert_eq!(package_table.buckets, expected_package_table.buckets);
161 for (node, expected_node) in
162 package_table.nodes.iter().zip(expected_package_table.nodes.iter())
163 {
164 assert_eq!(node.package_name, expected_node.package_name);
165 assert_eq!(node.package_id, expected_node.package_id);
166 assert_eq!(node.boolean_start_index, expected_node.boolean_start_index);
167 assert_eq!(node.next_offset, expected_node.next_offset);
168 }
169 }
170 }
171