diff --git a/include/al_utilities.h b/include/al_utilities.h new file mode 100644 index 00000000..ed17a4f5 --- /dev/null +++ b/include/al_utilities.h @@ -0,0 +1,24 @@ +#ifndef AL_UTILITIES_H +#define AL_UTILITIES_H + +/** + * General-purpose utility functions for the Access Layer. +*/ + +#include +#include + +namespace utilities { + +/** + * Convert a vector of strings into a C-style array of strings. + * + * @param paths the vector of strings to convert + * @param path_list a pointer to the C-style array to create + * @param size a pointer to an integer to store the size of the created array + */ +void copy_stringvector_to_c_list(const std::vector& paths, char*** path_list, int* size); + +} // namespace utilities + +#endif // AL_UTILITIES_H diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2800528e..3aa7a997 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -6,6 +6,7 @@ target_sources( al PRIVATE al_context.cpp al_const.cpp al_exception.cpp + al_utilities.cpp no_backend.cpp memory_backend.cpp ascii_backend.cpp diff --git a/src/al_utilities.cpp b/src/al_utilities.cpp new file mode 100644 index 00000000..f2faa4c7 --- /dev/null +++ b/src/al_utilities.cpp @@ -0,0 +1,21 @@ +#include "al_utilities.h" + +#include +#include + +namespace utilities { + +void copy_stringvector_to_c_list(const std::vector& paths, char*** path_list, int* size) { + *size = static_cast(paths.size()); + + if (*size == 0) { + *path_list = nullptr; + return; + } + + *path_list = static_cast(malloc(*size * sizeof(char*))); + for (int i = 0; i < *size; ++i) { + (*path_list)[i] = strdup(paths[i].c_str()); + } +} +} // namespace utilities \ No newline at end of file diff --git a/src/hdf5/hdf5_reader.cpp b/src/hdf5/hdf5_reader.cpp index 04dcd7e0..a4a553b9 100644 --- a/src/hdf5/hdf5_reader.cpp +++ b/src/hdf5/hdf5_reader.cpp @@ -8,6 +8,7 @@ #include #include #include +#include "al_utilities.h" #define MAX_LENGTH 200 #define HOMOGENEOUS_TIME_FIELD_NAME "ids_properties&homogeneous_time" @@ -1604,11 +1605,7 @@ void HDF5Reader::list_filled_paths(const char* dataobjectname, char*** path_list } // Create the C-style array: - *size = variables.size(); - *path_list = (char**) malloc(*size * sizeof(char*)); - for (int i=0; i < *size; ++i) { - (*path_list)[i] = strdup(variables[i].c_str()); - } + utilities::copy_stringvector_to_c_list(variables, path_list, size); } herr_t HDF5Reader::iterate_callback(hid_t loc_id, const char *name, const H5L_info_t *info, void *callback_data) diff --git a/src/uda/uda_backend.cpp b/src/uda/uda_backend.cpp index 74a538b3..d380251e 100644 --- a/src/uda/uda_backend.cpp +++ b/src/uda/uda_backend.cpp @@ -17,6 +17,7 @@ #include #include +#include "al_utilities.h" using namespace semver::literals; @@ -1258,6 +1259,54 @@ std::vector read_occurrences(NodeReader *node) { return occurrences; } +std::vector read_filled_paths(NodeReader *node) { + std::vector paths; + + // Read the shape + std::vector shape_vec(1); + uda_capnp_read_shape(node, shape_vec.data()); + const size_t total_bytes = std::accumulate(shape_vec.begin(), shape_vec.end(), 1, std::multiplies()); + + // For empty buffers, return empty list + if (total_bytes == 0) { + return paths; + } + + const char *name = uda_capnp_read_name(node); + if (name != nullptr && std::string(name) != "filled_paths") { + throw imas::uda::CacheException("Invalid node: " + std::string(name)); + } + + bool eos = uda_capnp_read_is_eos(node); + if (!eos) { + throw imas::uda::CacheException("UDA backend does not currently handle streamed data"); + } + + size_t num_slices = uda_capnp_read_num_slices(node); + if (num_slices != 1) { + throw imas::uda::CacheException("Incorrect number of slices for filled_paths node"); + } + + // Read the data slice + size_t slice_size = uda_capnp_read_slice_size(node, 0); + if (slice_size != total_bytes) { + throw imas::uda::CacheException("Slice size does not match total bytes for filled_paths"); + } + + std::vector buffer(total_bytes); + uda_capnp_read_data(node, 0, buffer.data()); + + const char* ptr = buffer.data(); + const char* end = ptr + total_bytes; + + while (ptr < end && *ptr != '\0') { + std::string path(ptr); + paths.push_back(path); + ptr += strlen(ptr) + 1; + } + + return paths; +} } // anon namespace void UDABackend::get_occurrences(Context* ctx, const char* ids_name, int** occurrences_list, int* size) { @@ -1311,5 +1360,67 @@ void UDABackend::get_occurrences(Context* ctx, const char* ids_name, int** occur } void UDABackend::list_filled_paths(Context* ctx, const char* dataobjectname, char*** path_list, int* size) { - throw ALBackendException("list_filled_paths is not implemented in the UDA Backend", LOG); + + *size = 0; + *path_list = nullptr; + + if (access_local_) { + return local_backend_->list_filled_paths(ctx, dataobjectname, path_list, size); + } + + if (verbose_) { + std::cout << "UDABackend list_filled_paths\n"; + } + + auto query = ctx->getURI().query; + std::string backend = get_backend(query); + if (backend != "hdf5") { + throw ALException("UDABackend only supports HDF5 backend for list_filled_paths API", LOG); + } + + query.remove("backend"); + query.remove("cache_mode"); + query.remove("verbose"); + std::string dd_version = query.get("dd_version").value_or(dd_version_); + query.set("dd_version", dd_version); + std::string uri = "imas:" + backend + "?" + query.to_string(); + + std::stringstream ss; + + ss << plugin_ + << "::listFilledPaths(" + << "uri='" << uri << "'" + << ", ids='" << dataobjectname << "'" + << ")"; + + const std::string directive = ss.str(); + + if (verbose_) { + std::cout << "UDABackend request: " << directive << "\n"; + } + + try { + const uda::Result& result = uda_client_.get(directive, ""); + + if (result.errorCode() == 0 && result.uda_type() == UDA_TYPE_CAPNP) { + const char* data = result.raw_data(); + const size_t result_size = result.size(); + + // If buffer is empty, return empty path list + if (result_size == 0) { + return; + } + + const auto tree = uda_capnp_deserialise(data, result_size); + const auto root = uda_capnp_read_root(tree); + + auto paths = read_filled_paths(root); + // Allocate and copy paths to C list + utilities::copy_stringvector_to_c_list(paths, path_list, size); + + uda_capnp_free_tree_reader(tree); + } + } catch (const uda::UDAException& ex) { + throw ALException(ex.what(), LOG); + } }