Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions include/al_utilities.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#ifndef AL_UTILITIES_H
#define AL_UTILITIES_H

/**
* General-purpose utility functions for the Access Layer.
*/

#include <vector>
#include <string>

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<std::string>& paths, char*** path_list, int* size);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest size of type size_t*, not int*


} // namespace utilities

#endif // AL_UTILITIES_H
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 21 additions & 0 deletions src/al_utilities.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include "al_utilities.h"

#include <cstdlib>
#include <cstring>

namespace utilities {

void copy_stringvector_to_c_list(const std::vector<std::string>& paths, char*** path_list, int* size) {
*size = static_cast<int>(paths.size());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you agree with my suggestion above, this will be size_t.
casting is not needed here, and it can mask errors. So remove casting here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @paulotex for pointing out, I will test with required change and update.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@paulotex I checked your suggestion, this int* (for size) is inherited from al_low_level and has impact on all other backends. I would suggest to implement it in separate branch.
@maarten-ic please share your thoughts.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC al_lowlevel is using int as its integer data type everywhere. I expect that this is for compatibility reasons, since the functions need to be callable from Java, Python, Fortran and C as well. I wouldn't change this interface as it could cause unexpected issues in all HLIs.

I agree that casting from std::size_t -> int is dangerous when the value is >= 2^31. However, for our use case the value is always between 0 and the maximum number of variables in an IDS (currently ~3,800 for the summary IDS in DD 4.1.0) and will never be anywhere close to the danger zone 😉
If you want to ensure that the code doesn't cause trouble, you could add a check that 0 <= paths.size() && paths.size < INT_MAX (https://en.cppreference.com/w/cpp/types/climits.html), but I don't think that's necessary


if (*size == 0) {
*path_list = nullptr;
return;
}

*path_list = static_cast<char**>(malloc(*size * sizeof(char*)));
for (int i = 0; i < *size; ++i) {
(*path_list)[i] = strdup(paths[i].c_str());
}
}
} // namespace utilities
7 changes: 2 additions & 5 deletions src/hdf5/hdf5_reader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <stdlib.h>
#include <limits>
#include <boost/algorithm/string.hpp>
#include "al_utilities.h"

#define MAX_LENGTH 200
#define HOMOGENEOUS_TIME_FIELD_NAME "ids_properties&homogeneous_time"
Expand Down Expand Up @@ -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)
Expand Down
113 changes: 112 additions & 1 deletion src/uda/uda_backend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include <string>
#include <random>
#include "al_utilities.h"

using namespace semver::literals;

Expand Down Expand Up @@ -1258,6 +1259,54 @@ std::vector<int> read_occurrences(NodeReader *node) {
return occurrences;
}

std::vector<std::string> read_filled_paths(NodeReader *node) {
std::vector<std::string> paths;

// Read the shape
std::vector<size_t> 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<size_t>());

// 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<char> 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) {
Expand Down Expand Up @@ -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);
}
}
Loading