/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright by The HDF Group. * * All rights reserved. * * * * This file is part of HDF5. The full HDF5 copyright notice, including * * terms governing use, modification, and redistribution, is contained in * * the COPYING file, which can be found at the root of the source code * * distribution tree, or in https://www.hdfgroup.org/licenses. * * If you do not have access to either file, you may request a copy from * * help@hdfgroup.org. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * Programmer: Matthew Larson * September 9, 2025 * * Purpose: Test the R-tree spatial indexing implementation. */ #include "h5test.h" /* * This file needs to access private datatypes from the H5RT package. */ #define H5RT_FRIEND /*suppress error about including H5RTpkg */ #include "H5RTpkg.h" /* R-tree package */ #define H5D_FRIEND /*suppress error about including H5Dpkg */ #define H5D_TESTING #include "H5Dpkg.h" /* Datasets */ #define RTREE_TEST_BASE_COORD 10000 #define RTREE_TEST_BASE_SIZE 1000 #define RTREE_TEST_CREATE_RANK 8 #define RTREE_TEST_CREATE_NUM_COUNTS 4 static const char *FILENAME[] = {"vds_rtree_src", /* 0: Source file for VDS mappings */ "vds_rtree_dapl", /* 1: DAPL test file */ "vds_rtree_threshold", /* 2: Threshold test file */ "vds_rtree_rw", /* 3: Read/write test file */ "vds_rtree_empty", /* 4: Empty slice test file */ NULL}; #define FILENAME_BUF_SIZE 1024 #define RTREE_DAPL_VDS_NAME "vdset" #define RTREE_DAPL_DATASET_DIM1 10 #define RTREE_DAPL_DATASET_DIM2 10 #define RTREE_MAX_TEST_MAPPINGS (H5D_VIRTUAL_TREE_THRESHOLD + 100) static const size_t test_counts[RTREE_TEST_CREATE_NUM_COUNTS] = {H5D_VIRTUAL_TREE_THRESHOLD, 100, 1000, 10000}; /* Helper function to generate leaf data */ static H5RT_leaf_t *generate_leaves(int rank, size_t leaf_count); /* Helper function to free leaf data */ static void free_leaves(H5RT_leaf_t *leaves, size_t leaf_count); /* For manual verification of r-tree results */ static H5RT_leaf_t **manual_search(H5RT_leaf_t *leaves, size_t leaf_count, int rank, hsize_t min[], hsize_t max[], size_t *results_count); /* Helper function to compare r-tree search results to linear search */ static herr_t verify_rtree_search(H5RT_result_set_t *result_set, H5RT_leaf_t *leaves, size_t leaf_count, hsize_t min[], hsize_t max[], int rank); /* Helper to create and initialize virtual dset in a file */ static hid_t create_virtual_dataset(hid_t file_id, hid_t dapl_id, int num_mappings, hid_t src_fapl); static herr_t verify_rtree_search(H5RT_result_set_t *result_set, H5RT_leaf_t *leaves, size_t leaf_count, hsize_t min[], hsize_t max[], int rank) { H5RT_leaf_t **manual_results = NULL; size_t num_manual_results = 0; herr_t ret_value = SUCCEED; assert(result_set); /* Perform manual search for comparison */ manual_results = manual_search(leaves, leaf_count, rank, min, max, &num_manual_results); /* Check equality - result_set is never NULL now */ if (num_manual_results != result_set->count) { puts("R-tree search and manual search found different number of results"); ret_value = FAIL; goto done; } if (num_manual_results > 0) { /* Order of results in each list may differ, so we need to check each result individually */ for (size_t i = 0; i < num_manual_results; i++) { H5RT_leaf_t *manual_leaf = manual_results[i]; bool found = false; /* Check if this manual result is in the r-tree results */ for (size_t j = 0; j < result_set->count; j++) { if (result_set->results[j] == manual_leaf) { found = true; break; } } if (!found) { puts("R-tree search missing a result found in manual search"); ret_value = FAIL; goto done; } } } done: if (manual_results) free(manual_results); return ret_value; } /* Helper function to generate leaf data */ static H5RT_leaf_t * generate_leaves(int rank, size_t leaf_count) { H5RT_leaf_t *ret_value = NULL; assert(rank > 0); assert(leaf_count > 0); if ((ret_value = calloc(leaf_count, sizeof(H5RT_leaf_t))) == NULL) goto done; for (size_t i = 0; i < leaf_count; i++) { /* Initialize leaf with dynamic coordinate allocation */ if (H5RT_leaf_init(&ret_value[i], rank, (void *)1) < 0) { /* Clean up already initialized leaves */ for (size_t j = 0; j < i; j++) { H5RT_leaf_cleanup(&ret_value[j]); } free(ret_value); ret_value = NULL; goto done; } /* Set coordinates */ for (int d = 0; d < rank; d++) { hsize_t min_coord = (hsize_t)rand() % RTREE_TEST_BASE_COORD; hsize_t size = 1 + (hsize_t)rand() % RTREE_TEST_BASE_SIZE; ret_value[i].min[d] = min_coord; ret_value[i].max[d] = min_coord + size; ret_value[i].mid[d] = (ret_value[i].max[d] + ret_value[i].min[d]) / 2; } } done: return ret_value; } /* Helper function to free leaf data */ static void free_leaves(H5RT_leaf_t *leaves, size_t leaf_count) { if (!leaves) return; for (size_t i = 0; i < leaf_count; i++) { H5RT_leaf_cleanup(&leaves[i]); } free(leaves); } static H5RT_leaf_t ** manual_search(H5RT_leaf_t *leaves, size_t leaf_count, int rank, hsize_t min[], hsize_t max[], size_t *results_count) { H5RT_leaf_t **ret_value = NULL; H5RT_leaf_t **results = NULL; assert(leaves); assert(results_count); /* Allocate maximum possible results size * May need to optimize if this makes testing times impractical */ if ((results = calloc(leaf_count, sizeof(H5RT_leaf_t *))) == NULL) goto done; for (size_t i = 0; i < leaf_count; i++) { if (H5RT__leaves_intersect(rank, min, max, leaves[i].min, leaves[i].max)) { results[(*results_count)++] = &leaves[i]; } } ret_value = results; done: if (!ret_value && results) free(results); return results; } /*------------------------------------------------------------------------- * Function: test_rtree_create * * Purpose: Test basic R-tree creation and cleanup * * Return: Success: SUCCEED * Failure: FAIL * *------------------------------------------------------------------------- */ static herr_t test_rtree_create(void) { H5RT_t *tree = NULL; size_t leaf_count = 0; H5RT_leaf_t *leaves = NULL; TESTING("R-tree creation"); srand(0); for (int cnt_idx = 0; cnt_idx < RTREE_TEST_CREATE_NUM_COUNTS; cnt_idx++) { leaf_count = test_counts[cnt_idx]; for (int rank = 1; rank < RTREE_TEST_CREATE_RANK; rank++) { /* Create the data to populate the r-tree */ if ((leaves = generate_leaves(rank, leaf_count)) == NULL) FAIL_STACK_ERROR; if ((tree = H5RT_create(rank, leaves, leaf_count)) == NULL) FAIL_STACK_ERROR; /* Ownership of memory has transferred */ /* leaves is now NULL */ if (H5RT_free(tree) < 0) FAIL_STACK_ERROR; } } PASSED(); return SUCCEED; error: if (leaves) free_leaves(leaves, leaf_count); return FAIL; } /*------------------------------------------------------------------------- * Function: test_rtree_search * * Purpose: Test R-tree spatial query operations * * Return: Success: SUCCEED * Failure: FAIL * *------------------------------------------------------------------------- */ static herr_t test_rtree_search(void) { H5RT_t *tree = NULL; size_t leaf_count = 0; H5RT_leaf_t *leaves = NULL; H5RT_leaf_t *leaves_temp = NULL; H5RT_result_set_t *result_set = NULL; hsize_t min[H5S_MAX_RANK]; hsize_t max[H5S_MAX_RANK]; hsize_t size = 0; TESTING("R-tree spatial queries"); srand(0); for (int cnt_idx = 0; cnt_idx < RTREE_TEST_CREATE_NUM_COUNTS; cnt_idx++) { leaf_count = test_counts[cnt_idx]; for (int rank = 1; rank < RTREE_TEST_CREATE_RANK; rank++) { memset(min, 0, H5S_MAX_RANK * sizeof(hsize_t)); memset(max, 0, H5S_MAX_RANK * sizeof(hsize_t)); /* Create data */ if ((leaves = generate_leaves(rank, leaf_count)) == NULL) FAIL_STACK_ERROR; /* Create tree */ leaves_temp = leaves; if ((tree = H5RT_create(rank, leaves, leaf_count)) == NULL) FAIL_STACK_ERROR; /* Ownership is transferred - leaves is now NULL */ /* Setup search criteria */ for (int r = 0; r < rank; r++) { min[r] = (hsize_t)(rand() % RTREE_TEST_BASE_COORD); size = 1 + (hsize_t)(rand() % RTREE_TEST_BASE_SIZE); max[r] = min[r] + size; } /* Perform r-tree search */ if (H5RT_search(tree, min, max, &result_set) < 0) FAIL_STACK_ERROR; /* Verify that results are equivalent to a manual search */ if (verify_rtree_search(result_set, leaves_temp, leaf_count, min, max, rank) < 0) { FAIL_STACK_ERROR; } /* Free search results */ if (H5RT_free_results(result_set) < 0) FAIL_STACK_ERROR; result_set = NULL; if (H5RT_free(tree) < 0) FAIL_STACK_ERROR; } } PASSED(); return SUCCEED; error: if (result_set) H5RT_free_results(result_set); if (leaves) free_leaves(leaves, leaf_count); if (tree) H5RT_free(tree); return FAIL; } /*------------------------------------------------------------------------- * Function: test_rtree_copy * * Purpose: Test R-tree deep copy functionality * * Return: Success: SUCCEED * Failure: FAIL * *------------------------------------------------------------------------- */ static herr_t test_rtree_copy(void) { H5RT_t *tree = NULL; H5RT_t *tree_copy = NULL; size_t leaf_count = 0; H5RT_leaf_t *leaves = NULL; H5RT_result_set_t *result_set = NULL; hsize_t min[H5S_MAX_RANK]; hsize_t max[H5S_MAX_RANK]; hsize_t size = 0; TESTING("R-tree copy"); srand(0); for (int cnt_idx = 0; cnt_idx < RTREE_TEST_CREATE_NUM_COUNTS; cnt_idx++) { leaf_count = test_counts[cnt_idx]; for (int rank = 1; rank < RTREE_TEST_CREATE_RANK; rank++) { memset(min, 0, H5S_MAX_RANK * sizeof(hsize_t)); memset(max, 0, H5S_MAX_RANK * sizeof(hsize_t)); /* Create data */ if ((leaves = generate_leaves(rank, leaf_count)) == NULL) FAIL_STACK_ERROR; /* Create original tree */ if ((tree = H5RT_create(rank, leaves, leaf_count)) == NULL) FAIL_STACK_ERROR; /* Ownership is transferred */ /* leaves is now NULL */ /* Deep copy the tree */ if ((tree_copy = H5RT_copy(tree)) == NULL) FAIL_STACK_ERROR; /* Delete the original tree */ if (H5RT_free(tree) < 0) FAIL_STACK_ERROR; tree = NULL; /* Setup search criteria */ for (int r = 0; r < rank; r++) { min[r] = (hsize_t)(rand() % RTREE_TEST_BASE_COORD); size = 1 + (hsize_t)(rand() % RTREE_TEST_BASE_SIZE); max[r] = min[r] + size; } /* Perform search on copied tree */ if (H5RT_search(tree_copy, min, max, &result_set) < 0) FAIL_STACK_ERROR; /* Verify that results are equivalent to a manual search */ if (verify_rtree_search(result_set, tree_copy->leaves, leaf_count, min, max, rank) < 0) { H5RT_free_results(result_set); FAIL_STACK_ERROR; } /* Free search results */ if (H5RT_free_results(result_set) < 0) FAIL_STACK_ERROR; result_set = NULL; if (H5RT_free(tree_copy) < 0) FAIL_STACK_ERROR; tree_copy = NULL; } } PASSED(); return SUCCEED; error: if (result_set) H5RT_free_results(result_set); if (leaves) free_leaves(leaves, leaf_count); if (tree) H5RT_free(tree); if (tree_copy) H5RT_free(tree_copy); return FAIL; } /*------------------------------------------------------------------------- * Function: create_virtual_dataset * * Purpose: Helper function to create a 1D virtual dataset with mappings * * Return: Success: dataset ID * Failure: H5I_INVALID_HID * *------------------------------------------------------------------------- */ static hid_t create_virtual_dataset(hid_t file_id, hid_t dapl_id, int num_mappings, hid_t src_fapl) { hid_t vspace_id = H5I_INVALID_HID; hid_t srcspace_id = H5I_INVALID_HID; hid_t vsel_id = H5I_INVALID_HID; hid_t srcfile_id = H5I_INVALID_HID; hid_t srcdset_id = H5I_INVALID_HID; hid_t vdset_id = H5I_INVALID_HID; hid_t dcpl_id = H5I_INVALID_HID; hsize_t vdims[1] = {(hsize_t)num_mappings}; hsize_t srcdims[1] = {1}; hsize_t start[1], count[1]; char srcdset_name[256]; char srcfilename[FILENAME_BUF_SIZE]; char srcfilename_map[FILENAME_BUF_SIZE]; int wdata; int i; /* Generate VFD-specific source filenames */ h5_fixname(FILENAME[0], src_fapl, srcfilename, sizeof(srcfilename)); h5_fixname_printf(FILENAME[0], src_fapl, srcfilename_map, sizeof(srcfilename_map)); /* Create 1D virtual dataset space */ if ((vspace_id = H5Screate_simple(1, vdims, NULL)) < 0) goto error; /* Create 1D source dataset space (single element) */ if ((srcspace_id = H5Screate_simple(1, srcdims, NULL)) < 0) goto error; /* Create dataset creation property list */ if ((dcpl_id = H5Pcreate(H5P_DATASET_CREATE)) < 0) goto error; /* Create source file - use actual filename, not the mapping version */ if ((srcfile_id = H5Fcreate(srcfilename, H5F_ACC_TRUNC, H5P_DEFAULT, src_fapl)) < 0) goto error; /* Create multiple source dsets and add virtual mappings */ for (i = 0; i < num_mappings; i++) { sprintf(srcdset_name, "%d_src_dset", i); /* Create source dataset */ if ((srcdset_id = H5Dcreate2(srcfile_id, srcdset_name, H5T_NATIVE_INT, srcspace_id, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0) goto error; /* Write data to source dataset (value equals index) */ wdata = i; if (H5Dwrite(srcdset_id, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &wdata) < 0) goto error; /* Create hyperslab selection for virtual dataset (one element at position i) */ if ((vsel_id = H5Scopy(vspace_id)) < 0) goto error; start[0] = (hsize_t)i; count[0] = 1; if (H5Sselect_hyperslab(vsel_id, H5S_SELECT_SET, start, NULL, count, NULL) < 0) goto error; /* Add virtual mapping - use the printf-escaped version for VDS mapping */ if (H5Pset_virtual(dcpl_id, vsel_id, srcfilename_map, srcdset_name, srcspace_id) < 0) goto error; /* Close source dataset and selection */ if (H5Dclose(srcdset_id) < 0) goto error; if (H5Sclose(vsel_id) < 0) goto error; srcdset_id = H5I_INVALID_HID; vsel_id = H5I_INVALID_HID; } /* Create virtual dataset */ if ((vdset_id = H5Dcreate2(file_id, RTREE_DAPL_VDS_NAME, H5T_NATIVE_INT, vspace_id, H5P_DEFAULT, dcpl_id, dapl_id)) < 0) goto error; /* Cleanup */ if (H5Sclose(vspace_id) < 0) goto error; if (H5Sclose(srcspace_id) < 0) goto error; if (H5Fclose(srcfile_id) < 0) goto error; if (H5Pclose(dcpl_id) < 0) goto error; return vdset_id; error: /* Cleanup */ H5E_BEGIN_TRY { H5Sclose(vspace_id); H5Sclose(srcspace_id); H5Sclose(vsel_id); H5Dclose(srcdset_id); H5Fclose(srcfile_id); H5Dclose(vdset_id); H5Pclose(dcpl_id); } H5E_END_TRY; return FAIL; } /*------------------------------------------------------------------------- * Function: test_rtree_existence_helper * * Purpose: Test helper to verify that r-tree existence on a dataset * matches what is expected * * Return: Success: SUCCEED * Failure: FAIL * *------------------------------------------------------------------------- */ static herr_t test_rtree_existence_helper(hid_t vdset_id, bool expect_tree, bool *correct_out) { herr_t ret_value = SUCCEED; H5D_t *dset = NULL; H5O_storage_virtual_t *storage = NULL; assert(correct_out); *correct_out = false; /* Get the dataset object - this is using internal API for testing */ if (NULL == (dset = (H5D_t *)H5VL_object(vdset_id))) { ret_value = FAIL; goto done; } if (dset->shared->layout.type != H5D_VIRTUAL) { ret_value = FAIL; goto done; } /* Get the virtual storage structure */ storage = &(dset->shared->layout.storage.u.virt); /* Verify tree existence matches expectation */ if (expect_tree) { if (storage->tree == NULL) { puts("Expected spatial tree to exist but it was NULL"); *correct_out = false; goto done; } if (storage->not_in_tree_nused > 0 && storage->not_in_tree_list == NULL) { puts("Expected not_in_tree_list array to exist but it was NULL"); *correct_out = false; goto done; } } else { if (storage->tree != NULL) { puts("Expected spatial tree to be NULL but it exists"); *correct_out = false; goto done; } if (storage->not_in_tree_list != NULL || storage->not_in_tree_nused > 0) { puts("Expected not_in_tree_list to be empty but it exists"); *correct_out = false; goto done; } } *correct_out = true; done: return ret_value; } /*------------------------------------------------------------------------- * Function: test_rtree_dapl * * Purpose: Test R-tree options on the DAPL * * Return: Success: SUCCEED * Failure: FAIL * *------------------------------------------------------------------------- */ static herr_t test_rtree_dapl(bool use_tree, bool read_init, hid_t vds_fapl, hid_t src_fapl) { hid_t file_id = H5I_INVALID_HID; hid_t dapl_id = H5I_INVALID_HID; hid_t vdset_id = H5I_INVALID_HID; int rbuf[RTREE_MAX_TEST_MAPPINGS]; int wbuf[RTREE_MAX_TEST_MAPPINGS]; bool tree_correct = false; char test_str[256]; char vfilename[FILENAME_BUF_SIZE]; /* Inverse of use_tree for re-open part of test */ bool use_tree_inverse = !use_tree; memset(test_str, 0, sizeof(test_str)); if (snprintf(test_str, sizeof(test_str), "spatial tree option %s", use_tree ? "enabled" : "disabled") < 0) FAIL_STACK_ERROR; if (read_init) { strncat(test_str, " with read initialization", sizeof(test_str) - strlen(test_str) - 1); } else { strncat(test_str, " with write initialization", sizeof(test_str) - strlen(test_str) - 1); } TESTING(test_str); memset(rbuf, 0, sizeof(int) * RTREE_MAX_TEST_MAPPINGS); memset(wbuf, 0, sizeof(int) * RTREE_MAX_TEST_MAPPINGS); /* Generate VFD-specific filename for VDS file */ h5_fixname(FILENAME[1], vds_fapl, vfilename, sizeof(vfilename)); /* One-time setup */ if ((file_id = H5Fcreate(vfilename, H5F_ACC_TRUNC, H5P_DEFAULT, vds_fapl)) < 0) FAIL_STACK_ERROR; if ((dapl_id = H5Pcreate(H5P_DATASET_ACCESS)) < 0) FAIL_STACK_ERROR; /* Create virtual dataset with enough mappings to use tree */ if ((vdset_id = create_virtual_dataset(file_id, dapl_id, RTREE_MAX_TEST_MAPPINGS, src_fapl)) < 0) FAIL_STACK_ERROR; if (H5Dclose(vdset_id) < 0) FAIL_STACK_ERROR; /* Set the spatial tree property */ if (H5Pset_virtual_spatial_tree(dapl_id, use_tree) < 0) FAIL_STACK_ERROR; if ((vdset_id = H5Dopen2(file_id, RTREE_DAPL_VDS_NAME, dapl_id)) < 0) FAIL_STACK_ERROR; /* Read/write the entire virtual dataset to force tree initialization */ if (read_init) { if (H5Dread(vdset_id, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, rbuf) < 0) FAIL_STACK_ERROR; } else { if (H5Dwrite(vdset_id, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, wbuf) < 0) FAIL_STACK_ERROR; } /* Verify tree existence matches expectation */ if (test_rtree_existence_helper(vdset_id, use_tree, &tree_correct) < 0) FAIL_STACK_ERROR; if (!tree_correct) FAIL_STACK_ERROR; /* Close the dataset and re-open it with the opposite value set in DAPL */ if (H5Dclose(vdset_id) < 0) FAIL_STACK_ERROR; vdset_id = H5I_INVALID_HID; if (H5Fclose(file_id) < 0) FAIL_STACK_ERROR; file_id = H5I_INVALID_HID; if (H5Pclose(dapl_id) < 0) FAIL_STACK_ERROR; dapl_id = H5I_INVALID_HID; tree_correct = false; memset(rbuf, 0, sizeof(int) * RTREE_MAX_TEST_MAPPINGS); if ((dapl_id = H5Pcreate(H5P_DATASET_ACCESS)) < 0) FAIL_STACK_ERROR; if (H5Pset_virtual_spatial_tree(dapl_id, use_tree_inverse) < 0) FAIL_STACK_ERROR; if ((file_id = H5Fopen(vfilename, H5F_ACC_RDWR, vds_fapl)) < 0) FAIL_STACK_ERROR; if ((vdset_id = H5Dopen2(file_id, RTREE_DAPL_VDS_NAME, dapl_id)) < 0) FAIL_STACK_ERROR; /* Read/write the entire virtual dataset to force tree initialization */ if (read_init) { if (H5Dread(vdset_id, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, rbuf) < 0) FAIL_STACK_ERROR; } else { if (H5Dwrite(vdset_id, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, wbuf) < 0) FAIL_STACK_ERROR; } /* Verify tree existence matches expectation after re-open */ if (test_rtree_existence_helper(vdset_id, use_tree_inverse, &tree_correct) < 0) FAIL_STACK_ERROR; if (!tree_correct) FAIL_STACK_ERROR; /* Cleanup */ if (H5Dclose(vdset_id) < 0) FAIL_STACK_ERROR; if (H5Pclose(dapl_id) < 0) FAIL_STACK_ERROR; if (H5Fclose(file_id) < 0) FAIL_STACK_ERROR; PASSED(); return SUCCEED; error: H5E_BEGIN_TRY { H5Dclose(vdset_id); H5Pclose(dapl_id); H5Fclose(file_id); } H5E_END_TRY; return FAIL; } /*------------------------------------------------------------------------- * Function: test_rtree_threshold * * Purpose: Test that threshold controls r-tree usage properly * * Return: Success: SUCCEED * Failure: FAIL * *------------------------------------------------------------------------- */ static herr_t test_rtree_threshold(bool use_tree, hid_t vds_fapl, hid_t src_fapl) { hid_t file_id = H5I_INVALID_HID; hid_t dapl_id = H5I_INVALID_HID; hid_t vdset_id = H5I_INVALID_HID; int rbuf[RTREE_MAX_TEST_MAPPINGS]; char vfilename[FILENAME_BUF_SIZE]; /* Internal values for introspection */ H5D_t *dset = NULL; H5O_storage_virtual_t *storage = NULL; const char *test_str = use_tree ? "threshold behavior with tree enabled" : "threshold behavior with tree disabled"; TESTING(test_str); /* Generate VFD-specific filename for VDS file */ h5_fixname(FILENAME[2], vds_fapl, vfilename, sizeof(vfilename)); /* Test cases: below threshold, at threshold, above threshold */ int test_cases[3] = {H5D_VIRTUAL_TREE_THRESHOLD - 1, H5D_VIRTUAL_TREE_THRESHOLD, RTREE_MAX_TEST_MAPPINGS}; for (int test_idx = 0; test_idx < 3; test_idx++) { int num_mappings = test_cases[test_idx]; bool expect_tree; /* Determine expected tree behavior based on threshold and use_tree setting */ /* Tree is created only when: tree_enabled AND num_mappings >= threshold */ expect_tree = (use_tree && (num_mappings >= H5D_VIRTUAL_TREE_THRESHOLD)); if ((file_id = H5Fcreate(vfilename, H5F_ACC_TRUNC, H5P_DEFAULT, vds_fapl)) < 0) FAIL_STACK_ERROR; if ((dapl_id = H5Pcreate(H5P_DATASET_ACCESS)) < 0) FAIL_STACK_ERROR; /* Set the spatial tree property */ if (H5Pset_virtual_spatial_tree(dapl_id, use_tree) < 0) FAIL_STACK_ERROR; /* Create virtual dataset with specified number of mappings */ if ((vdset_id = create_virtual_dataset(file_id, dapl_id, num_mappings, src_fapl)) < 0) FAIL_STACK_ERROR; /* Read the virtual dataset to force initialization */ if (H5Dread(vdset_id, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, rbuf) < 0) FAIL_STACK_ERROR; /* Verify data pattern (each element should equal its index) */ for (int i = 0; i < num_mappings; i++) { if (rbuf[i] != i) { printf("%d mappings: Data mismatch at [%d]: expected %d, got %d\n", num_mappings, i, i, rbuf[i]); FAIL_STACK_ERROR; } } /* Get the dataset object for introspection */ if (NULL == (dset = (H5D_t *)H5VL_object(vdset_id))) FAIL_STACK_ERROR; if (dset->shared->layout.type != H5D_VIRTUAL) FAIL_STACK_ERROR; /* Get the virtual storage structure */ storage = &(dset->shared->layout.storage.u.virt); /* Verify tree existence matches expectation */ if (expect_tree) { if (storage->tree == NULL) { printf("%d mappings: Expected spatial tree to exist but it was NULL\n", num_mappings); FAIL_STACK_ERROR; } /* not_in_tree_list can be NULL if all mappings fit in tree - this is OK */ /* Just verify consistency: if nused > 0, then list should exist */ if (storage->not_in_tree_nused > 0 && storage->not_in_tree_list == NULL) { printf("%d mappings: Expected not_in_tree_list array to exist but it was NULL\n", num_mappings); FAIL_STACK_ERROR; } } else { if (storage->tree != NULL) { printf("%d mappings: Expected spatial tree to be NULL but it exists\n", num_mappings); FAIL_STACK_ERROR; } if (storage->not_in_tree_list != NULL) { printf("%d mappings: Expected not_in_tree_list array to be NULL but it exists\n", num_mappings); FAIL_STACK_ERROR; } } /* Cleanup */ if (H5Dclose(vdset_id) < 0) FAIL_STACK_ERROR; if (H5Pclose(dapl_id) < 0) FAIL_STACK_ERROR; if (H5Fclose(file_id) < 0) FAIL_STACK_ERROR; vdset_id = H5I_INVALID_HID; dapl_id = H5I_INVALID_HID; file_id = H5I_INVALID_HID; } PASSED(); return SUCCEED; error: H5E_BEGIN_TRY { H5Dclose(vdset_id); H5Pclose(dapl_id); H5Fclose(file_id); } H5E_END_TRY; return FAIL; } /*------------------------------------------------------------------------- * Function: test_rtree_rw * * Purpose: Test that dataset reads/writes produce correctly values * with rtree on/off * * Return: Success: SUCCEED * Failure: FAIL * *------------------------------------------------------------------------- */ static herr_t test_rtree_rw(bool use_tree, hid_t vds_fapl, hid_t src_fapl) { hid_t file_id = H5I_INVALID_HID; hid_t dapl_id = H5I_INVALID_HID; hid_t vdset_id = H5I_INVALID_HID; hid_t space_id = H5I_INVALID_HID; hsize_t wdims = RTREE_MAX_TEST_MAPPINGS / 2; int rbuf[RTREE_MAX_TEST_MAPPINGS]; int wbuf[RTREE_MAX_TEST_MAPPINGS]; int num_mappings = RTREE_MAX_TEST_MAPPINGS; char vfilename[FILENAME_BUF_SIZE]; const char *test_str = use_tree ? "R/W behavior with tree enabled" : "R/W behavior with tree disabled"; TESTING(test_str); memset(rbuf, 0, sizeof(int) * RTREE_MAX_TEST_MAPPINGS); memset(wbuf, 0, sizeof(int) * RTREE_MAX_TEST_MAPPINGS); /* Generate VFD-specific filename for VDS file */ h5_fixname(FILENAME[3], vds_fapl, vfilename, sizeof(vfilename)); if ((file_id = H5Fcreate(vfilename, H5F_ACC_TRUNC, H5P_DEFAULT, vds_fapl)) < 0) FAIL_STACK_ERROR; if ((dapl_id = H5Pcreate(H5P_DATASET_ACCESS)) < 0) FAIL_STACK_ERROR; /* Set the spatial tree property */ if (H5Pset_virtual_spatial_tree(dapl_id, use_tree) < 0) FAIL_STACK_ERROR; /* Create virtual dataset with specified number of mappings */ if ((vdset_id = create_virtual_dataset(file_id, dapl_id, num_mappings, src_fapl)) < 0) FAIL_STACK_ERROR; /* Verify initial read values (each element should equal its index) */ if (H5Dread(vdset_id, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, rbuf) < 0) FAIL_STACK_ERROR; for (int i = 0; i < num_mappings; i++) { if (rbuf[i] != i) { printf("%d mappings: Data mismatch at [%d]: expected %d, got %d\n", num_mappings, i, i, rbuf[i]); FAIL_STACK_ERROR; } } /* Write to first half of dataset with 2*index */ for (int i = 0; i < num_mappings / 2; i++) wbuf[i] = 2 * i; if ((space_id = H5Screate_simple(1, (const hsize_t *)&wdims, NULL)) < 0) FAIL_STACK_ERROR; if (H5Sselect_hyperslab(space_id, H5S_SELECT_SET, (const hsize_t *)&(hsize_t){0}, NULL, (const hsize_t *)&wdims, NULL) < 0) FAIL_STACK_ERROR; if (H5Dwrite(vdset_id, H5T_NATIVE_INT, space_id, space_id, H5P_DEFAULT, wbuf) < 0) FAIL_STACK_ERROR; /* Read back entire dataset and verify values */ if (H5Dread(vdset_id, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, rbuf) < 0) FAIL_STACK_ERROR; for (int i = 0; i < num_mappings; i++) { int expected = (i < num_mappings / 2) ? (2 * i) : i; if (rbuf[i] != expected) { printf("%d mappings: Post-write data mismatch at [%d]: expected %d, got %d\n", num_mappings, i, expected, rbuf[i]); FAIL_STACK_ERROR; } } /* Cleanup */ if (H5Dclose(vdset_id) < 0) FAIL_STACK_ERROR; if (H5Pclose(dapl_id) < 0) FAIL_STACK_ERROR; if (H5Fclose(file_id) < 0) FAIL_STACK_ERROR; if (H5Sclose(space_id) < 0) FAIL_STACK_ERROR; PASSED(); return SUCCEED; error: H5E_BEGIN_TRY { H5Sclose(space_id); H5Dclose(vdset_id); H5Pclose(dapl_id); H5Fclose(file_id); } H5E_END_TRY; return FAIL; } /*------------------------------------------------------------------------- * Function: test_vds_empty_slice * * Purpose: Test zero-element read from virtual dataset with the r-tree enabled * * Return: Success: SUCCEED * Failure: FAIL * *------------------------------------------------------------------------- */ static herr_t test_vds_empty_slice(hid_t vds_fapl, hid_t src_fapl) { hid_t srcfile_id = H5I_INVALID_HID; hid_t vdsfile_id = H5I_INVALID_HID; hid_t raw_dset_id = H5I_INVALID_HID; hid_t vds_dset_id = H5I_INVALID_HID; hid_t src_space = H5I_INVALID_HID; hid_t vds_space = H5I_INVALID_HID; hid_t vds_sel = H5I_INVALID_HID; hid_t mem_space = H5I_INVALID_HID; hid_t file_space = H5I_INVALID_HID; hid_t dcpl = H5I_INVALID_HID; hsize_t src_dim = 1; hsize_t vds_dim = 100; hsize_t start = 0; hsize_t count = 0; int wdata = 42; int read_buf = -1; char srcfilename[FILENAME_BUF_SIZE]; char srcfilename_map[FILENAME_BUF_SIZE]; char vdsfilename[FILENAME_BUF_SIZE]; size_t i; TESTING("zero-element read from virtual dataset with r-tree"); /* Generate VFD-specific filenames */ h5_fixname(FILENAME[0], src_fapl, srcfilename, sizeof(srcfilename)); h5_fixname_printf(FILENAME[0], src_fapl, srcfilename_map, sizeof(srcfilename_map)); h5_fixname(FILENAME[4], vds_fapl, vdsfilename, sizeof(vdsfilename)); /* Create source file with single dataset */ if ((srcfile_id = H5Fcreate(srcfilename, H5F_ACC_TRUNC, H5P_DEFAULT, src_fapl)) < 0) FAIL_STACK_ERROR; if ((src_space = H5Screate_simple(1, &src_dim, NULL)) < 0) FAIL_STACK_ERROR; if ((raw_dset_id = H5Dcreate2(srcfile_id, "src", H5T_NATIVE_INT, src_space, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT)) < 0) FAIL_STACK_ERROR; if (H5Dwrite(raw_dset_id, H5T_NATIVE_INT, H5S_ALL, H5S_ALL, H5P_DEFAULT, &wdata) < 0) FAIL_STACK_ERROR; if ((vdsfile_id = H5Fcreate(vdsfilename, H5F_ACC_TRUNC, H5P_DEFAULT, vds_fapl)) < 0) FAIL_STACK_ERROR; if ((vds_space = H5Screate_simple(1, &vds_dim, NULL)) < 0) FAIL_STACK_ERROR; if ((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0) FAIL_STACK_ERROR; /* Add mappings and create VDS */ for (i = 0; i < vds_dim; i++) { if ((vds_sel = H5Scopy(vds_space)) < 0) FAIL_STACK_ERROR; start = i; count = 1; if (H5Sselect_hyperslab(vds_sel, H5S_SELECT_SET, &start, NULL, &count, NULL) < 0) FAIL_STACK_ERROR; if (H5Pset_virtual(dcpl, vds_sel, srcfilename_map, "src", src_space) < 0) FAIL_STACK_ERROR; if (H5Sclose(vds_sel) < 0) FAIL_STACK_ERROR; vds_sel = H5I_INVALID_HID; } if ((vds_dset_id = H5Dcreate2(vdsfile_id, "vds", H5T_NATIVE_INT, vds_space, H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0) FAIL_STACK_ERROR; /* Trigger tree initialization by reading a single element */ start = 0; count = 1; if ((mem_space = H5Screate_simple(1, &count, NULL)) < 0) FAIL_STACK_ERROR; if ((file_space = H5Dget_space(vds_dset_id)) < 0) FAIL_STACK_ERROR; if (H5Sselect_hyperslab(file_space, H5S_SELECT_SET, &start, NULL, &count, NULL) < 0) FAIL_STACK_ERROR; if (H5Dread(vds_dset_id, H5T_NATIVE_INT, mem_space, file_space, H5P_DEFAULT, &read_buf) < 0) FAIL_STACK_ERROR; if (H5Sclose(mem_space) < 0) FAIL_STACK_ERROR; if (H5Sclose(file_space) < 0) FAIL_STACK_ERROR; mem_space = H5I_INVALID_HID; file_space = H5I_INVALID_HID; /* Attempt zero-element read */ count = 0; if ((mem_space = H5Screate_simple(1, &count, NULL)) < 0) FAIL_STACK_ERROR; if ((file_space = H5Dget_space(vds_dset_id)) < 0) FAIL_STACK_ERROR; if (H5Sselect_hyperslab(file_space, H5S_SELECT_SET, &start, NULL, &count, NULL) < 0) FAIL_STACK_ERROR; if (H5Dread(vds_dset_id, H5T_NATIVE_INT, mem_space, file_space, H5P_DEFAULT, &read_buf) < 0) FAIL_STACK_ERROR; /* Cleanup */ if (H5Sclose(mem_space) < 0) FAIL_STACK_ERROR; if (H5Sclose(file_space) < 0) FAIL_STACK_ERROR; if (H5Sclose(src_space) < 0) FAIL_STACK_ERROR; if (H5Sclose(vds_space) < 0) FAIL_STACK_ERROR; if (H5Pclose(dcpl) < 0) FAIL_STACK_ERROR; if (H5Dclose(raw_dset_id) < 0) FAIL_STACK_ERROR; if (H5Dclose(vds_dset_id) < 0) FAIL_STACK_ERROR; if (H5Fclose(srcfile_id) < 0) FAIL_STACK_ERROR; if (H5Fclose(vdsfile_id) < 0) FAIL_STACK_ERROR; PASSED(); return SUCCEED; error: H5E_BEGIN_TRY { H5Sclose(mem_space); H5Sclose(file_space); H5Sclose(src_space); H5Sclose(vds_space); H5Sclose(vds_sel); H5Pclose(dcpl); H5Dclose(raw_dset_id); H5Dclose(vds_dset_id); H5Fclose(srcfile_id); H5Fclose(vdsfile_id); } H5E_END_TRY; return FAIL; } /*------------------------------------------------------------------------- * Function: main * * Purpose: Test the R-tree functionality * * Return: Success: EXIT_SUCCESS * Failure: EXIT_FAILURE * *------------------------------------------------------------------------- */ int main(void) { int nerrors = 0; hid_t vds_fapl = H5I_INVALID_HID; hid_t src_fapl = H5I_INVALID_HID; char srcfilename[FILENAME_BUF_SIZE]; char vfilename[FILENAME_BUF_SIZE]; char threshfilename[FILENAME_BUF_SIZE]; char rwfilename[FILENAME_BUF_SIZE]; printf("Testing R-tree spatial indexing...\n"); H5open(); /* Create file access property lists for VDS and source files */ if ((vds_fapl = h5_fileaccess()) < 0) TEST_ERROR; if ((src_fapl = h5_fileaccess()) < 0) TEST_ERROR; /* Run core R-tree tests */ nerrors += test_rtree_create() < 0 ? 1 : 0; nerrors += test_rtree_search() < 0 ? 1 : 0; nerrors += test_rtree_copy() < 0 ? 1 : 0; /* Test spatial tree with DAPL property enabled */ nerrors += test_rtree_dapl(true, true, vds_fapl, src_fapl) < 0 ? 1 : 0; nerrors += test_rtree_dapl(true, false, vds_fapl, src_fapl) < 0 ? 1 : 0; nerrors += test_rtree_dapl(false, true, vds_fapl, src_fapl) < 0 ? 1 : 0; nerrors += test_rtree_dapl(false, false, vds_fapl, src_fapl) < 0 ? 1 : 0; /* Test the mapping count threshold */ nerrors += test_rtree_threshold(true, vds_fapl, src_fapl) < 0 ? 1 : 0; nerrors += test_rtree_threshold(false, vds_fapl, src_fapl) < 0 ? 1 : 0; nerrors += test_rtree_rw(true, vds_fapl, src_fapl) < 0 ? 1 : 0; nerrors += test_rtree_rw(false, vds_fapl, src_fapl) < 0 ? 1 : 0; nerrors += test_vds_empty_slice(vds_fapl, src_fapl) < 0 ? 1 : 0; if (nerrors) goto error; /* Generate VFD-specific filenames for cleanup */ h5_fixname(FILENAME[0], src_fapl, srcfilename, sizeof(srcfilename)); h5_fixname(FILENAME[1], vds_fapl, vfilename, sizeof(vfilename)); h5_fixname(FILENAME[2], vds_fapl, threshfilename, sizeof(threshfilename)); h5_fixname(FILENAME[3], vds_fapl, rwfilename, sizeof(rwfilename)); char emptyfilename[FILENAME_BUF_SIZE]; h5_fixname(FILENAME[4], vds_fapl, emptyfilename, sizeof(emptyfilename)); H5E_BEGIN_TRY { H5Fdelete(srcfilename, src_fapl); H5Fdelete(vfilename, vds_fapl); H5Fdelete(threshfilename, vds_fapl); H5Fdelete(rwfilename, vds_fapl); H5Fdelete(emptyfilename, vds_fapl); } H5E_END_TRY; if (H5Pclose(vds_fapl) < 0) TEST_ERROR; if (H5Pclose(src_fapl) < 0) TEST_ERROR; printf("All R-tree tests passed.\n"); return EXIT_SUCCESS; error: H5E_BEGIN_TRY { H5Pclose(vds_fapl); H5Pclose(src_fapl); } H5E_END_TRY; printf("***** R-TREE TESTS FAILED *****\n"); return EXIT_FAILURE; }