@@ -233,6 +233,17 @@ impl Chunk {
233
233
}
234
234
235
235
impl Chunking {
236
+ /// Creates a reverse map from content IDs to checksums
237
+ fn create_content_id_map (
238
+ map : & IndexMap < String , ContentID > ,
239
+ ) -> IndexMap < ContentID , Vec < & String > > {
240
+ let mut rmap = IndexMap :: < ContentID , Vec < & String > > :: new ( ) ;
241
+ for ( checksum, contentid) in map. iter ( ) {
242
+ rmap. entry ( Rc :: clone ( contentid) ) . or_default ( ) . push ( checksum) ;
243
+ }
244
+ rmap
245
+ }
246
+
236
247
/// Generate an initial single chunk.
237
248
pub fn new ( repo : & ostree:: Repo , rev : & str ) -> Result < Self > {
238
249
// Find the target commit
@@ -310,21 +321,17 @@ impl Chunking {
310
321
return Ok ( ( ) ) ;
311
322
}
312
323
313
- // Reverses `contentmeta.map` i.e. contentid -> Vec<checksum>
314
- let mut rmap = IndexMap :: < ContentID , Vec < & String > > :: new ( ) ;
315
- for ( checksum, contentid) in meta. map . iter ( ) {
316
- rmap. entry ( Rc :: clone ( contentid) ) . or_default ( ) . push ( checksum) ;
317
- }
318
-
319
324
// Create exclusive chunks first if specified
320
325
let mut processed_specific_components = BTreeSet :: new ( ) ;
321
326
if let Some ( specific_meta) = specific_contentmeta {
327
+ let specific_map = Self :: create_content_id_map ( & specific_meta. map ) ;
328
+
322
329
for component in & specific_meta. sizes {
323
330
let mut chunk = Chunk :: new ( & component. meta . name ) ;
324
331
chunk. packages = vec ! [ component. meta. name. to_string( ) ] ;
325
332
326
333
// Move all objects belonging to this exclusive component
327
- if let Some ( objects) = rmap . get ( & component. meta . identifier ) {
334
+ if let Some ( objects) = specific_map . get ( & component. meta . identifier ) {
328
335
for & obj in objects {
329
336
self . remainder . move_obj ( & mut chunk, obj) ;
330
337
}
@@ -353,6 +360,8 @@ impl Chunking {
353
360
. cloned ( )
354
361
. collect ( ) ;
355
362
363
+ let rmap = Self :: create_content_id_map ( & meta. map ) ;
364
+
356
365
// Process regular components with bin packing if we have remaining layers
357
366
if let Some ( remaining) = NonZeroU32 :: new ( self . remaining ( ) ) {
358
367
let start = Instant :: now ( ) ;
@@ -1221,4 +1230,183 @@ mod test {
1221
1230
1222
1231
Ok ( ( ) )
1223
1232
}
1233
+
1234
+ #[ test]
1235
+ fn test_process_mapping_specific_components_contain_correct_objects ( ) -> Result < ( ) > {
1236
+ // This test validates that specific components get their own dedicated layers
1237
+ // and that their objects are properly isolated from regular package layers
1238
+
1239
+ // Setup: Create 5 packages
1240
+ // - pkg1, pkg2: Will be marked as "specific components" (get their own layers)
1241
+ // - pkg3, pkg4, pkg5: Regular packages (will be bin-packed together)
1242
+ let packages = [
1243
+ ( 1 , 100 , 50000 ) , // pkg1 - SPECIFIC COMPONENT
1244
+ ( 2 , 200 , 40000 ) , // pkg2 - SPECIFIC COMPONENT
1245
+ ( 3 , 300 , 30000 ) , // pkg3 - regular package
1246
+ ( 4 , 400 , 20000 ) , // pkg4 - regular package
1247
+ ( 5 , 500 , 10000 ) , // pkg5 - regular package
1248
+ ] ;
1249
+
1250
+ let ( contentmeta, mut system_metadata, mut specific_components_meta, mut chunking) =
1251
+ setup_exclusive_test ( & packages, 8 , None ) ?;
1252
+
1253
+ // Create object mappings
1254
+ // - system_objects_map: Contains ALL objects in the system (both specific and regular)
1255
+ // - specific_components_objects_map: Contains ONLY objects from specific components
1256
+ let mut system_objects_map = IndexMap :: new ( ) ;
1257
+ let mut specific_components_objects_map = IndexMap :: new ( ) ;
1258
+
1259
+ // SPECIFIC COMPONENT 1 (pkg1): owns 3 objects
1260
+ let pkg1_objects = [ "checksum_1_a" , "checksum_1_b" , "checksum_1_c" ] ;
1261
+ for obj in & pkg1_objects {
1262
+ system_objects_map. insert ( obj. to_string ( ) , contentmeta[ 0 ] . meta . identifier . clone ( ) ) ;
1263
+ specific_components_objects_map
1264
+ . insert ( obj. to_string ( ) , contentmeta[ 0 ] . meta . identifier . clone ( ) ) ;
1265
+ }
1266
+
1267
+ // SPECIFIC COMPONENT 2 (pkg2): owns 2 objects
1268
+ let pkg2_objects = [ "checksum_2_a" , "checksum_2_b" ] ;
1269
+ for obj in & pkg2_objects {
1270
+ system_objects_map. insert ( obj. to_string ( ) , contentmeta[ 1 ] . meta . identifier . clone ( ) ) ;
1271
+ specific_components_objects_map
1272
+ . insert ( obj. to_string ( ) , contentmeta[ 1 ] . meta . identifier . clone ( ) ) ;
1273
+ }
1274
+
1275
+ // REGULAR PACKAGE 1 (pkg3): owns 2 objects
1276
+ let pkg3_objects = [ "checksum_3_a" , "checksum_3_b" ] ;
1277
+ for obj in & pkg3_objects {
1278
+ system_objects_map. insert ( obj. to_string ( ) , contentmeta[ 2 ] . meta . identifier . clone ( ) ) ;
1279
+ }
1280
+
1281
+ // REGULAR PACKAGE 2 (pkg4): owns 1 object
1282
+ let pkg4_objects = [ "checksum_4_a" ] ;
1283
+ for obj in & pkg4_objects {
1284
+ system_objects_map. insert ( obj. to_string ( ) , contentmeta[ 3 ] . meta . identifier . clone ( ) ) ;
1285
+ }
1286
+
1287
+ // REGULAR PACKAGE 3 (pkg5): owns 3 objects
1288
+ let pkg5_objects = [ "checksum_5_a" , "checksum_5_b" , "checksum_5_c" ] ;
1289
+ for obj in & pkg5_objects {
1290
+ system_objects_map. insert ( obj. to_string ( ) , contentmeta[ 4 ] . meta . identifier . clone ( ) ) ;
1291
+ }
1292
+
1293
+ // Set up metadata
1294
+ system_metadata. map = system_objects_map;
1295
+ specific_components_meta. map = specific_components_objects_map;
1296
+ specific_components_meta. sizes = vec ! [ contentmeta[ 0 ] . clone( ) , contentmeta[ 1 ] . clone( ) ] ;
1297
+
1298
+ // Initialize: Add ALL objects to the remainder chunk before processing
1299
+ // This includes both specific component objects and regular package objects
1300
+ // because process_mapping needs to move them from remainder to their final layers
1301
+ for ( checksum, _) in & system_metadata. map {
1302
+ chunking. remainder . content . insert (
1303
+ RcStr :: from ( checksum. as_str ( ) ) ,
1304
+ (
1305
+ 1000 ,
1306
+ vec ! [ Utf8PathBuf :: from( format!( "/path/to/{}" , checksum) ) ] ,
1307
+ ) ,
1308
+ ) ;
1309
+ chunking. remainder . size += 1000 ;
1310
+ }
1311
+
1312
+ // Process the mapping
1313
+ // - system_metadata contains ALL objects in the system
1314
+ // - specific_components_meta tells process_mapping which objects belong to specific components
1315
+ chunking. process_mapping (
1316
+ & system_metadata,
1317
+ & Some ( NonZeroU32 :: new ( 8 ) . unwrap ( ) ) ,
1318
+ None ,
1319
+ Some ( & specific_components_meta) ,
1320
+ ) ?;
1321
+
1322
+ // VALIDATION PART 1: Specific components get their own dedicated chunks
1323
+ assert ! (
1324
+ chunking. chunks. len( ) >= 2 ,
1325
+ "Should have at least 2 chunks for specific components"
1326
+ ) ;
1327
+
1328
+ // Specific Component Layer 1: pkg1 only
1329
+ let specific_component_1_layer = & chunking. chunks [ 0 ] ;
1330
+ assert_eq ! ( specific_component_1_layer. name, "pkg1" ) ;
1331
+ assert_eq ! ( specific_component_1_layer. packages, vec![ "pkg1" ] ) ;
1332
+ assert_eq ! ( specific_component_1_layer. content. len( ) , 3 ) ;
1333
+ for obj in & pkg1_objects {
1334
+ assert ! (
1335
+ specific_component_1_layer. content. contains_key( * obj) ,
1336
+ "Specific component 1 layer should contain {}" ,
1337
+ obj
1338
+ ) ;
1339
+ }
1340
+
1341
+ // Specific Component Layer 2: pkg2 only
1342
+ let specific_component_2_layer = & chunking. chunks [ 1 ] ;
1343
+ assert_eq ! ( specific_component_2_layer. name, "pkg2" ) ;
1344
+ assert_eq ! ( specific_component_2_layer. packages, vec![ "pkg2" ] ) ;
1345
+ assert_eq ! ( specific_component_2_layer. content. len( ) , 2 ) ;
1346
+ for obj in & pkg2_objects {
1347
+ assert ! (
1348
+ specific_component_2_layer. content. contains_key( * obj) ,
1349
+ "Specific component 2 layer should contain {}" ,
1350
+ obj
1351
+ ) ;
1352
+ }
1353
+
1354
+ // VALIDATION PART 2: Specific component layers contain NO regular package objects
1355
+ for specific_layer in & chunking. chunks [ 0 ..2 ] {
1356
+ for obj in pkg3_objects
1357
+ . iter ( )
1358
+ . chain ( & pkg4_objects)
1359
+ . chain ( & pkg5_objects)
1360
+ {
1361
+ assert ! (
1362
+ !specific_layer. content. contains_key( * obj) ,
1363
+ "Specific component layer '{}' should NOT contain regular package object {}" ,
1364
+ specific_layer. name,
1365
+ obj
1366
+ ) ;
1367
+ }
1368
+ }
1369
+
1370
+ // VALIDATION PART 3: Regular package layers contain NO specific component objects
1371
+ let regular_package_layers = & chunking. chunks [ 2 ..] ;
1372
+ for regular_layer in regular_package_layers {
1373
+ for obj in pkg1_objects. iter ( ) . chain ( & pkg2_objects) {
1374
+ assert ! (
1375
+ !regular_layer. content. contains_key( * obj) ,
1376
+ "Regular package layer should NOT contain specific component object {}" ,
1377
+ obj
1378
+ ) ;
1379
+ }
1380
+ }
1381
+
1382
+ // VALIDATION PART 4: All regular package objects are in some regular layer
1383
+ let mut found_regular_objects = BTreeSet :: new ( ) ;
1384
+ for regular_layer in regular_package_layers {
1385
+ for ( obj, _) in & regular_layer. content {
1386
+ found_regular_objects. insert ( obj. as_ref ( ) ) ;
1387
+ }
1388
+ }
1389
+
1390
+ for obj in pkg3_objects
1391
+ . iter ( )
1392
+ . chain ( & pkg4_objects)
1393
+ . chain ( & pkg5_objects)
1394
+ {
1395
+ assert ! (
1396
+ found_regular_objects. contains( * obj) ,
1397
+ "Regular package object {} should be in some regular layer" ,
1398
+ obj
1399
+ ) ;
1400
+ }
1401
+
1402
+ // VALIDATION PART 5: All objects moved from remainder
1403
+ assert_eq ! (
1404
+ chunking. remainder. content. len( ) ,
1405
+ 0 ,
1406
+ "All objects should be moved from remainder"
1407
+ ) ;
1408
+ assert_eq ! ( chunking. remainder. size, 0 , "Remainder size should be 0" ) ;
1409
+
1410
+ Ok ( ( ) )
1411
+ }
1224
1412
}
0 commit comments