Program Listing for File h5read_processed.cxx

↰ Return to documentation for file (dx2/h5/h5read_processed.cxx)

#include "dx2/h5/h5read_processed.hpp"

namespace h5read_processed_utils {

#pragma region Callbacks
herr_t scan_group_callback(hid_t loc_id, const char *name, const H5L_info2_t *,
                           void *op_data) {
  auto *context = static_cast<GroupScanContext *>(op_data);

  // Get object info
  H5O_info2_t obj_info;
  if (H5Oget_info_by_name3(loc_id, name, &obj_info, H5O_INFO_BASIC,
                           H5P_DEFAULT) < 0) {
    return 0; // Skip unreadable entries
  }

  if (obj_info.type == H5O_TYPE_DATASET) {
    context->datasets.push_back(context->group_name + "/" + std::string(name));
  }

  return 0;
}

herr_t group_iterator(hid_t loc_id, const char *name, const H5L_info2_t *info,
                      void *op_data) {
  TraverseData *traverse_data = static_cast<TraverseData *>(op_data);
  std::string full_path =
      traverse_data->current_path.empty()
          ? "/" + std::string(name)
          : traverse_data->current_path + "/" + std::string(name);

  // std::cout << "Visiting: " << full_path << std::endl;

  // Ensure this path is not visited twice
  if (traverse_data->visited_groups->find(full_path) !=
      traverse_data->visited_groups->end()) {
    // std::cout << "Skipping already visited group: " << full_path <<
    // std::endl;
    return 0; // Continue iteration
  }

  // Get object info
  H5O_info2_t obj_info;
  if (H5Oget_info_by_name3(loc_id, name, &obj_info, H5O_INFO_BASIC,
                           H5P_DEFAULT) < 0) {
    dx2_log::error(fmt::format("Unable to get object info for: {}", full_path));
    return -1;
  }

  if (obj_info.type == H5O_TYPE_DATASET) {
    // std::cout << "Dataset found: " << full_path << std::endl;
    traverse_data->datasets->push_back(full_path);
  } else if (obj_info.type == H5O_TYPE_GROUP) {
    // std::cout << "Entering group: " << full_path << std::endl;

    // Mark the group as visited
    traverse_data->visited_groups->insert(full_path);

    // Open the group to recurse into it
    h5utils::H5Group group_id(H5Gopen2(loc_id, name, H5P_DEFAULT));
    if (group_id) {
      traverse_hdf5(group_id, full_path, *(traverse_data->datasets),
                    *(traverse_data->visited_groups));
    } else {
      dx2_log::error(fmt::format("Unable to open group: {}", full_path));
    }
  }

  return 0; // Continue iteration
}

#pragma endregion
#pragma region Group Traversal

void traverse_hdf5(hid_t loc_id, const std::string &path,
                   std::vector<std::string> &datasets,
                   std::unordered_set<std::string> &visited_groups) {
  // std::cout << "Traversing: " << (path.empty() ? "/" : path) << std::endl;

  TraverseData traverse_data = {&datasets, path, &visited_groups};
  H5Literate2(loc_id, H5_INDEX_NAME, H5_ITER_INC, NULL, group_iterator,
              &traverse_data);
}

#pragma endregion
} // namespace h5read_processed_utils

#pragma region Public API
std::vector<std::string> get_datasets_in_group(std::string_view filename,
                                               std::string_view group_name) {
  std::string fname(filename);
  std::string gpath(group_name);
  // Open file
  h5utils::H5File file(H5Fopen(fname.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT));
  if (!file) {
    throw std::runtime_error("Error: Unable to open file: " + fname);
  }

  // Open group
  h5utils::H5Group group(H5Gopen2(file, gpath.c_str(), H5P_DEFAULT));
  if (!group) {
    dx2_log::warning(fmt::format("Missing group '{}', skipping.", gpath));
    return {};
  }

  h5read_processed_utils::GroupScanContext context{gpath, {}};

  // Iterate over immediate children
  H5Literate2(group, H5_INDEX_NAME, H5_ITER_INC, nullptr,
              h5read_processed_utils::scan_group_callback, &context);

  return context.datasets;
}

std::vector<std::string>
get_datasets_in_group_recursive(std::string_view filename,
                                std::string_view group_name) {
  std::string fname(filename);
  std::string gpath(group_name);

  std::vector<std::string> datasets;
  std::unordered_set<std::string> visited_groups;

  // Open the HDF5 file
  h5utils::H5File file(H5Fopen(fname.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT));
  if (!file) {
    dx2_log::error(fmt::format("Unable to open file: {}", fname));
  }

  // Open the group
  // hid_t group = H5Gopen2(file, gpath.c_str(), H5P_DEFAULT);
  h5utils::H5Group group(H5Gopen2(file, gpath.c_str(), H5P_DEFAULT));
  if (!group) {
    dx2_log::warning(fmt::format("Missing group '{}', skipping.", gpath));
    return {};
  }

  // Start traversal from the group
  h5read_processed_utils::traverse_hdf5(group, gpath, datasets, visited_groups);

  return datasets;
}

void read_experiment_metadata(hid_t group_id,
                              std::vector<uint64_t> &experiment_ids,
                              std::vector<std::string> &identifiers) {
  if (H5Aexists(group_id, "experiment_ids") > 0) {
    h5utils::H5Attr attr(H5Aopen(group_id, "experiment_ids", H5P_DEFAULT));
    h5utils::H5Space space(H5Aget_space(attr));
    hssize_t num_elements = H5Sget_simple_extent_npoints(space);

    experiment_ids.resize(num_elements);
    if (H5Aread(attr, H5T_NATIVE_ULLONG, experiment_ids.data()) < 0) {
      dx2_log::warning(fmt::format(
          "Failed to read experiment_ids attribute ({} entries), skipping.",
          num_elements));
      experiment_ids.clear();
    }
  }

  if (H5Aexists(group_id, "identifiers") > 0) {
    h5utils::H5Attr attr(H5Aopen(group_id, "identifiers", H5P_DEFAULT));
    h5utils::H5Type type(H5Aget_type(attr));
    h5utils::H5Space space(H5Aget_space(attr));
    hssize_t num_elements = H5Sget_simple_extent_npoints(space);

    std::vector<char *> raw_strings(num_elements);
    if (H5Aread(attr, type, raw_strings.data()) < 0) {
      dx2_log::warning(fmt::format(
          "Failed to read identifiers attribute ({} entries), skipping.",
          num_elements));
      return;
    }

    // A null entry is a valid HDF5 variable-length string and reads as empty
    identifiers.clear();
    identifiers.reserve(num_elements);
    for (const char *raw : raw_strings) {
      identifiers.emplace_back(raw ? raw : "");
    }

    // Variable-length strings are allocated by HDF5 during the read
    H5Treclaim(type, space, H5P_DEFAULT, raw_strings.data());
  }
}

std::string get_dataset_name(std::string_view path) {
  size_t pos = path.find_last_of('/');
  if (pos == std::string_view::npos) {
    return std::string(path); // No '/' found, return the whole path
  }
  return std::string(path.substr(pos + 1));
}
#pragma endregion