• 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 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