From 86dd669583af85d6b66709ef12d778e24fbd3eee Mon Sep 17 00:00:00 2001 From: David Boehme Date: Tue, 7 Oct 2025 16:53:29 -0700 Subject: [PATCH] Add scalar size specifiers --- docs/sphinx/ApplicationAPI.rst | 33 +++++-- include/adiak.h | 20 ++++- src/adiak.c | 159 ++++++++++++++++++++++++++------- tests/test_application-api.cpp | 106 ++++++++++++++++++++-- tests/test_zerocopy.c | 12 +++ tests/testapp.c | 8 ++ 6 files changed, 294 insertions(+), 44 deletions(-) diff --git a/docs/sphinx/ApplicationAPI.rst b/docs/sphinx/ApplicationAPI.rst index 885a5ad..ba47b0f 100644 --- a/docs/sphinx/ApplicationAPI.rst +++ b/docs/sphinx/ApplicationAPI.rst @@ -165,26 +165,49 @@ when given specific STL containers as value, e.g. ``std::tuple`` for tuples or For detailed information on how to create each compound type, refer to the :cpp:enum:`adiak_type_t` documentation. +Scalar size specifiers +................................ + +Adiak internally stores scalar values as 32-bit integer, 64-bit integer, or +64-bit floating point values. Adiak automatically converts smaller integers +(such as ``short``) to ``int``, and ``float`` to ``double``. However, when types +other than (unsigned) ``int`` or ``double`` are used in compound types, you must +use a length specifier in the :cpp:func:`adiak_namevalue` type string argument +to indicate the exact input type, e.g. `"%f32"` for ``float`` or `"%i16"` +for ``signed short``. The available type specifiers are "%i8", "%i16", "%i32", +"%u8", "%u16", "%u32", "%f32", and "%f64". For 64-bit integers, use "%lld" or +"%llu". Examples: + +.. code-block:: c + + const unsigned char bytes[4] = { 32, 33, 34, 35 }; + const signed short smallints[4] = { -1024, 0, 1, 512 }; + const float floats[3] = { 0.42, 42.0, 42.42 }; + + adiak_namevalue("bytes", adiak_general, NULL, "{%u8}", bytes, 4); + adiak_namevalue("smallints", adiak_general, NULL, "[%i16]", smallints, 4); + adiak_namevalue("floats", adiak_general, NULL, "{%f32}", floats, 3); + .. _reference_values: Reference values ................................ Optionally, values can be stored as references, where Adiak does not copy the data -but only keeps a pointer to it. -Reference entries are available for string types and compound types (lists, sets, -etc.). Scalar types (int, double, etc.) cannot be stored as references. +but only keeps a pointer to it. +Reference entries are available for string types and compound types (lists, sets, +etc.). Scalar types (int, double, etc.) cannot be stored as references. Any objects stored as references must be retained in memory by the application throughout the lifetime of the program or until :cpp:func:`adiak_clean` is called. Currently, reference values must be created through the C API. To do so, -place a ``&`` in front of the type specifier in :cpp:func:`adiak_namevalue`, +place a ``&`` in front of the type specifier in :cpp:func:`adiak_namevalue`, e.g. ``&{%d}`` for a list of integers or ``&%s`` for a string. For compound types, the ``&`` can be applied to an inner type to create a shallow copy, e.g. ``{&%s}`` for a shallow-copy list of zero-copy strings. Examples: -.. code-block:: c +.. code-block:: c static const int array[9] = { -1, 2, -3, 4, 5, 4, 3, 2, 1 }; static const char* string_a = "A long string"; diff --git a/include/adiak.h b/include/adiak.h index 3a61151..0e90537 100644 --- a/include/adiak.h +++ b/include/adiak.h @@ -68,11 +68,17 @@ typedef enum { * that must be passed as the \a typestr argument in \ref adiak_namevalue for * values of the corresponding type. * + * For integer and floating point scalar values, an optional size specifier + * can be added in the typestring to specify the exact input type (8, 16, + * 32, or 64 bit). The available specifiers are i8, i16, i32, i64, u8, u16, + * u32, u64, f32, and f64. For example, use "%f32" for 32-bit float values, + * "%i16" for signed short (16-bit) integers, etc. + * * Type | Typestring | Value category * -----------------|--------------|--------------- * adiak_long | "%ld" | rational * adiak_ulong | "%lu" | rational - * adiak_int | "%d" | rational + * adiak_int | "%d", "%i" | rational * adiak_uint | "%u" | rational * adiak_double | "%f" | rational * adiak_date | "%D" | interval @@ -196,6 +202,8 @@ typedef struct adiak_datatype_t { int is_reference; /** \brief Number of sub-elements for reference container values */ int num_ref_elements; + /** \brief Size in bytes of the original input data (currently only for base types) */ + size_t num_bytes; } adiak_datatype_t; /** \brief Adiak value union @@ -240,7 +248,7 @@ typedef union adiak_value_t { long long v_longlong; } adiak_value_t; -static const adiak_datatype_t adiak_unset_datatype = { adiak_type_unset, adiak_numerical_unset, 0, 0, NULL, 0, 0 }; +static const adiak_datatype_t adiak_unset_datatype = { adiak_type_unset, adiak_numerical_unset, 0, 0, NULL, 0, 0, 0 }; /** * \} @@ -285,16 +293,22 @@ void adiak_fini(); * from the string specifiers shown in \ref adiak_type_t. The varargs contain the value * and, if needed, any additional parameters for the type (e.g., list length). * + * When scalar values other than (unsigned) \c int, \c long, or \c double are used in + * compound types, use a length specifier to indicate the exact type in \a typestr, + * e.g. "%f32" for 32-bit \c float or "%i16" for \c short. + * * Examples: * * \code * adiak_namevalue("numrecords", adiak_general, NULL, "%d", 10); - * * adiak_namevalue("buildcompiler", adiak_general, "build data", "%v", "gcc@4.7.3"); * * double gridvalues[] = { 5.4, 18.1, 24.0, 92.8 }; * adiak_namevalue("gridvals", adiak_general, NULL, "[%f]", gridvalues, 4); * + * unsigned char bytes[] = { 32, 33, 34, 35 }; + * adiak_namevalue("bytes", adiak_general, NULL, "{%u8}", bytes, 4); + * * struct { int pos; const char *val; } letters[3] = { {1, "a"}, {2, "b"}, {3, "c"} }; * adiak_namevalue("alphabet", adiak_general, NULL, "[(%d, %s)]", letters, 3, 2); * \endcode diff --git a/src/adiak.c b/src/adiak.c index 1628d17..4657c67 100644 --- a/src/adiak.c +++ b/src/adiak.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "adiak.h" #include "adiak_tool.h" @@ -64,25 +65,30 @@ static int measure_adiak_walltime; static int measure_adiak_systime; static int measure_adiak_cputime; -static adiak_datatype_t base_long = { adiak_long, adiak_rational, 0, 0, NULL, 0, 0 }; -static adiak_datatype_t base_ulong = { adiak_ulong, adiak_rational, 0, 0, NULL, 0, 0 }; -static adiak_datatype_t base_longlong = { adiak_longlong, adiak_rational, 0, 0, NULL, 0, 0 }; -static adiak_datatype_t base_ulonglong = { adiak_ulonglong, adiak_rational, 0, 0, NULL, 0, 0 }; -static adiak_datatype_t base_int = { adiak_int, adiak_rational, 0, 0, NULL, 0, 0 }; -static adiak_datatype_t base_uint = { adiak_uint, adiak_rational, 0, 0, NULL, 0, 0 }; -static adiak_datatype_t base_double = { adiak_double, adiak_rational, 0, 0, NULL, 0, 0 }; -static adiak_datatype_t base_date = { adiak_date, adiak_interval, 0, 0, NULL, 0, 0 }; -static adiak_datatype_t base_timeval = { adiak_timeval, adiak_interval, 0, 0, NULL, 0, 0 }; -static adiak_datatype_t base_version = { adiak_version, adiak_ordinal, 0, 0, NULL, 0, 0 }; -static adiak_datatype_t base_string = { adiak_string, adiak_ordinal, 0, 0, NULL, 0, 0 }; -static adiak_datatype_t base_catstring = { adiak_catstring, adiak_categorical, 0, 0, NULL, 0, 0 }; -static adiak_datatype_t base_jsonstring = { adiak_jsonstring, adiak_categorical, 0, 0, NULL, 0, 0 }; -static adiak_datatype_t base_path = { adiak_path, adiak_categorical, 0, 0, NULL, 0, 0 }; -static adiak_datatype_t base_version_ref = { adiak_version, adiak_ordinal, 0, 0, NULL, 1, 0 }; -static adiak_datatype_t base_string_ref = { adiak_string, adiak_ordinal, 0, 0, NULL, 1, 0 }; -static adiak_datatype_t base_catstring_ref = { adiak_catstring, adiak_categorical, 0, 0, NULL, 1, 0 }; -static adiak_datatype_t base_jsonstring_ref = { adiak_jsonstring, adiak_categorical, 0, 0, NULL, 1, 0 }; -static adiak_datatype_t base_path_ref = { adiak_path, adiak_categorical, 0, 0, NULL, 1, 0 }; +static adiak_datatype_t base_long = { adiak_long, adiak_rational, 0, 0, NULL, 0, 0, sizeof(long) }; +static adiak_datatype_t base_ulong = { adiak_ulong, adiak_rational, 0, 0, NULL, 0, 0, sizeof(unsigned long) }; +static adiak_datatype_t base_longlong = { adiak_longlong, adiak_rational, 0, 0, NULL, 0, 0, sizeof(long long) }; +static adiak_datatype_t base_ulonglong = { adiak_ulonglong, adiak_rational, 0, 0, NULL, 0, 0, sizeof(unsigned long long) }; +static adiak_datatype_t base_int = { adiak_int, adiak_rational, 0, 0, NULL, 0, 0, sizeof(int) }; +static adiak_datatype_t base_i8 = { adiak_int, adiak_rational, 0, 0, NULL, 0, 0, 1 }; +static adiak_datatype_t base_i16 = { adiak_int, adiak_rational, 0, 0, NULL, 0, 0, 2 }; +static adiak_datatype_t base_u8 = { adiak_uint, adiak_rational, 0, 0, NULL, 0, 0, 1 }; +static adiak_datatype_t base_u16 = { adiak_uint, adiak_rational, 0, 0, NULL, 0, 0, 2 }; +static adiak_datatype_t base_uint = { adiak_uint, adiak_rational, 0, 0, NULL, 0, 0, sizeof(unsigned int) }; +static adiak_datatype_t base_double = { adiak_double, adiak_rational, 0, 0, NULL, 0, 0, sizeof(double) }; +static adiak_datatype_t base_float = { adiak_double, adiak_rational, 0, 0, NULL, 0, 0, sizeof(float) }; +static adiak_datatype_t base_date = { adiak_date, adiak_interval, 0, 0, NULL, 0, 0, sizeof(long) }; +static adiak_datatype_t base_timeval = { adiak_timeval, adiak_interval, 0, 0, NULL, 0, 0, sizeof(struct timeval*) }; +static adiak_datatype_t base_version = { adiak_version, adiak_ordinal, 0, 0, NULL, 0, 0, sizeof(char*) }; +static adiak_datatype_t base_string = { adiak_string, adiak_ordinal, 0, 0, NULL, 0, 0, sizeof(char*) }; +static adiak_datatype_t base_catstring = { adiak_catstring, adiak_categorical, 0, 0, NULL, 0, 0, sizeof(char*) }; +static adiak_datatype_t base_jsonstring = { adiak_jsonstring, adiak_categorical, 0, 0, NULL, 0, 0, sizeof(char*) }; +static adiak_datatype_t base_path = { adiak_path, adiak_categorical, 0, 0, NULL, 0, 0, sizeof(char*) }; +static adiak_datatype_t base_version_ref = { adiak_version, adiak_ordinal, 0, 0, NULL, 1, 0, sizeof(char*) }; +static adiak_datatype_t base_string_ref = { adiak_string, adiak_ordinal, 0, 0, NULL, 1, 0, sizeof(char*) }; +static adiak_datatype_t base_catstring_ref = { adiak_catstring, adiak_categorical, 0, 0, NULL, 1, 0, sizeof(char*) }; +static adiak_datatype_t base_jsonstring_ref = { adiak_jsonstring, adiak_categorical, 0, 0, NULL, 1, 0, sizeof(char*) }; +static adiak_datatype_t base_path_ref = { adiak_path, adiak_categorical, 0, 0, NULL, 1, 0, sizeof(char*) }; static adiak_t* adiak_get_config(); static void adiak_register(int adiak_version, int category, @@ -91,6 +97,7 @@ static void adiak_register(int adiak_version, int category, int report_on_all_ranks, void *opaque_val); static int find_end_brace(const char *typestr, char endchar, int typestr_start, int typestr_end); +static int parse_scalar_len_spec(const char* typestr, int *pos); static adiak_datatype_t *parse_typestr(const char *typestr, va_list *ap); static adiak_datatype_t *parse_typestr_helper(const char *typestr, int typestr_start, int typestr_end, int is_reference, va_list *ap, int *new_typestr_start); @@ -397,10 +404,22 @@ int adiak_get_subval(adiak_datatype_t* t, adiak_value_t* val, int elem, adiak_da break; case adiak_int: case adiak_uint: - (*subval).v_int = *((int *) ptr); + switch ((*subtype)->num_bytes) { + case 1: + (*subval).v_int = *((int8_t *) ptr); + break; + case 2: + (*subval).v_int = *((int16_t *) ptr); + break; + default: + (*subval).v_int = *((int *) ptr); + } break; case adiak_double: - (*subval).v_double = *((double *) ptr); + if ((*subtype)->num_bytes == 4) + (*subval).v_double = *((float *) ptr); + else + (*subval).v_double = *((double *) ptr); break; case adiak_timeval: case adiak_version: @@ -543,6 +562,7 @@ static adiak_datatype_t *parse_typestr(const char *typestr, va_list *ap) static adiak_type_t toplevel_type(const char *typestr) { const char *cur = typestr; + int pos = 0; if (!cur) return adiak_type_unset; while (isspace(*cur) || *cur == ',' || *cur == '&') @@ -561,7 +581,8 @@ static adiak_type_t toplevel_type(const char *typestr) { } return adiak_type_unset; case 'd': return adiak_int; - case 'u': return adiak_uint; + case 'i': return parse_scalar_len_spec(cur, &pos) == 8 ? adiak_longlong : adiak_int; + case 'u': return parse_scalar_len_spec(cur, &pos) == 8 ? adiak_ulonglong : adiak_uint; case 'f': return adiak_double; case 'D': return adiak_date; case 't': return adiak_timeval; @@ -709,12 +730,10 @@ static int calc_size(adiak_datatype_t* datatype) return sizeof(long); case adiak_longlong: case adiak_ulonglong: - return sizeof(long long); case adiak_int: case adiak_uint: - return sizeof(int); case adiak_double: - return sizeof(double); + return (int) datatype->num_bytes; case adiak_timeval: return sizeof(struct timeval *); case adiak_version: @@ -756,11 +775,23 @@ static int copy_value(adiak_value_t *target, adiak_datatype_t *datatype, void *p return sizeof(long long); case adiak_int: case adiak_uint: - target->v_int = *((int *) ptr); - return sizeof(int); + switch (datatype->num_bytes) { + case 1: + target->v_int = *((int8_t *) ptr); + break; + case 2: + target->v_int = *((int16_t *) ptr); + break; + default: + target->v_int = *((int *) ptr); + } + return datatype->num_bytes; case adiak_double: - target->v_double= *((double *) ptr); - return sizeof(double); + if (datatype->num_bytes == 4) + target->v_double = *((float *) ptr); + else + target->v_double = *((double *) ptr); + return datatype->num_bytes; case adiak_timeval: { struct timeval* v = (struct timeval*) ptr; if (!datatype->is_reference) { @@ -804,6 +835,25 @@ static int copy_value(adiak_value_t *target, adiak_datatype_t *datatype, void *p return -1; } +static int parse_scalar_len_spec(const char* typestr, int *pos) +{ + if (typestr[(*pos)+1] == '8') { + *pos += 1; + return 1; + } else if (typestr[(*pos)+1] == '1' && typestr[(*pos)+2] == '6') { + *pos += 2; + return 2; + } else if (typestr[(*pos)+1] == '3' && typestr[(*pos)+2] == '2') { + *pos += 2; + return 4; + } else if (typestr[(*pos)+1] == '6' && typestr[(*pos)+2] == '4') { + *pos += 2; + return 8; + } else if (typestr[(*pos)+1] >= '0' && typestr[(*pos)+1] <= '9') + return -1; + return 0; +} + static adiak_datatype_t *parse_typestr_helper(const char *typestr, int typestr_start, int typestr_end, int is_reference, va_list *ap, int *new_typestr_start) @@ -900,10 +950,57 @@ static adiak_datatype_t *parse_typestr_helper(const char *typestr, int typestr_s t = is_long ? (is_longlong ? &base_longlong : &base_long) : &base_int; break; case 'u': - t = is_long ? (is_longlong ? &base_ulonglong : &base_ulong) : &base_uint; + switch (parse_scalar_len_spec(typestr, &cur)) { + case 0: + t = is_long ? (is_longlong ? &base_ulonglong : &base_ulong) : &base_uint; + break; + case 1: + t = &base_u8; + break; + case 2: + t = &base_u16; + break; + case 4: + t = &base_uint; + break; + case 8: + t = &base_ulonglong; + break; + default: + goto error; + } + break; + case 'i': + switch (parse_scalar_len_spec(typestr, &cur)) { + case 0: + case 4: + t = &base_int; + break; + case 1: + t = &base_i8; + break; + case 2: + t = &base_i16; + break; + case 8: + t = &base_longlong; + break; + default: + goto error; + } break; case 'f': - t = &base_double; + switch (parse_scalar_len_spec(typestr, &cur)) { + case 0: + case 8: + t = &base_double; + break; + case 4: + t = &base_float; + break; + default: + goto error; + } break; case 'D': t = &base_date; diff --git a/tests/test_application-api.cpp b/tests/test_application-api.cpp index f5022c3..7c9443d 100644 --- a/tests/test_application-api.cpp +++ b/tests/test_application-api.cpp @@ -3,6 +3,7 @@ #include "adiak.hpp" #include "adiak_tool.h" +#include #include #include #include @@ -225,14 +226,23 @@ TEST(AdiakApplicationAPI, C_CompoundTypes) { const double v_range[] = { -1.0, 1.0 }; const int v_ints[] = { 1, 2, 3 }; - const struct tuple_t { const char* str; long long i; } v_tuples[3] = { - { "one", 1ll }, { "two", 2ll }, { "three", 3ll } + const unsigned char v_u8s[] = { 3, 4, 5, 6 }; + const int16_t v_i16s[] = { 256, 512, -1024, 42 }; + const float v_f32s[] = { 1.5, 2.5, 3.5, 4.5, 5.5 }; + + const struct tuple_t { const char* str; long long i; } v_tuples[4] = { + { "one", 1ll }, { "two", 2ll }, { "three", 3ll }, { "four", 4ll } }; EXPECT_EQ(adiak_namevalue("c:range:double", adiak_general, NULL, "<%f>", v_range), 0); - EXPECT_EQ(adiak_namevalue("c:tuple", adiak_general, NULL, "(%s,%lld)", v_tuples, 2), 0); + EXPECT_EQ(adiak_namevalue("c:range:f64", adiak_general, NULL, "<%f64>", v_range), 0); + EXPECT_EQ(adiak_namevalue("c:tuple", adiak_general, NULL, "(%s,%lld,%f32)", v_tuples, 2), 0); EXPECT_EQ(adiak_namevalue("c:vec:int", adiak_general, NULL, "{%d}", v_ints, 3), 0); - EXPECT_EQ(adiak_namevalue("c:vec:tpl", adiak_general, NULL, "{(%s,%lld)}", v_tuples, 3, 2), 0); + EXPECT_EQ(adiak_namevalue("c:vec:i32", adiak_general, NULL, "{%i32}", v_ints, 3), 0); + EXPECT_EQ(adiak_namevalue("c:vec:tpl", adiak_general, NULL, "{(%s,%lld,%f32)}", v_tuples, 4, 2), 0); + EXPECT_EQ(adiak_namevalue("c:vec:u8", adiak_general, NULL, "{%u8}", v_u8s, 4), 0); + EXPECT_EQ(adiak_namevalue("c:vec:i16", adiak_general, NULL, "{%i16}", v_i16s, 4), 0); + EXPECT_EQ(adiak_namevalue("c:vec:f32", adiak_general, NULL, "{%f32}", v_f32s, 5), 0); adiak_datatype_t* dtype = nullptr; adiak_value_t* val = nullptr; @@ -252,6 +262,17 @@ TEST(AdiakApplicationAPI, C_CompoundTypes) EXPECT_EQ(subtype->dtype, adiak_type_t::adiak_double); EXPECT_EQ(subval.v_double, 1.0); + EXPECT_EQ(adiak_get_nameval("c:range:f64", &dtype, &val, &cat, &subcat), 0); + EXPECT_EQ(dtype->dtype, adiak_type_t::adiak_range); + EXPECT_EQ(cat, adiak_general); + EXPECT_EQ(adiak_num_subvals(dtype), 2); + EXPECT_EQ(adiak_get_subval(dtype, val, 0, &subtype, &subval), 0); + EXPECT_EQ(subtype->dtype, adiak_type_t::adiak_double); + EXPECT_EQ(subval.v_double, -1.0); + EXPECT_EQ(adiak_get_subval(dtype, val, 1, &subtype, &subval), 0); + EXPECT_EQ(subtype->dtype, adiak_type_t::adiak_double); + EXPECT_EQ(subval.v_double, 1.0); + EXPECT_EQ(adiak_get_nameval("c:vec:int", &dtype, &val, &cat, &subcat), 0); EXPECT_EQ(dtype->dtype, adiak_type_t::adiak_list); EXPECT_EQ(cat, adiak_general); @@ -263,10 +284,21 @@ TEST(AdiakApplicationAPI, C_CompoundTypes) EXPECT_EQ(subtype->dtype, adiak_type_t::adiak_int); EXPECT_EQ(subval.v_int, 3); - EXPECT_EQ(adiak_get_nameval("c:vec:tpl", &dtype, &val, &cat, &subcat), 0); + EXPECT_EQ(adiak_get_nameval("c:vec:i32", &dtype, &val, &cat, &subcat), 0); EXPECT_EQ(dtype->dtype, adiak_type_t::adiak_list); EXPECT_EQ(cat, adiak_general); EXPECT_EQ(adiak_num_subvals(dtype), 3); + EXPECT_EQ(adiak_get_subval(dtype, val, 0, &subtype, &subval), 0); + EXPECT_EQ(subtype->dtype, adiak_type_t::adiak_int); + EXPECT_EQ(subval.v_int, 1); + EXPECT_EQ(adiak_get_subval(dtype, val, 2, &subtype, &subval), 0); + EXPECT_EQ(subtype->dtype, adiak_type_t::adiak_int); + EXPECT_EQ(subval.v_int, 3); + + EXPECT_EQ(adiak_get_nameval("c:vec:tpl", &dtype, &val, &cat, &subcat), 0); + EXPECT_EQ(dtype->dtype, adiak_type_t::adiak_list); + EXPECT_EQ(cat, adiak_general); + EXPECT_EQ(adiak_num_subvals(dtype), 4); EXPECT_EQ(adiak_get_subval(dtype, val, 2, &subtype, &subval), 0); adiak_datatype_t *inner_subtype; adiak_value_t inner_subval; @@ -275,6 +307,39 @@ TEST(AdiakApplicationAPI, C_CompoundTypes) EXPECT_EQ(adiak_get_subval(subtype, &subval, 1, &inner_subtype, &inner_subval), 0); EXPECT_EQ(inner_subtype->dtype, adiak_type_t::adiak_longlong); EXPECT_EQ(inner_subval.v_longlong, 3ll); + + EXPECT_EQ(adiak_get_nameval("c:vec:u8", &dtype, &val, &cat, &subcat), 0); + EXPECT_EQ(dtype->dtype, adiak_type_t::adiak_list); + EXPECT_EQ(cat, adiak_general); + EXPECT_EQ(adiak_num_subvals(dtype), 4); + EXPECT_EQ(adiak_get_subval(dtype, val, 0, &subtype, &subval), 0); + EXPECT_EQ(subtype->dtype, adiak_type_t::adiak_uint); + EXPECT_EQ(subval.v_int, 3); + EXPECT_EQ(adiak_get_subval(dtype, val, 2, &subtype, &subval), 0); + EXPECT_EQ(subtype->dtype, adiak_type_t::adiak_uint); + EXPECT_EQ(subval.v_int, 5); + + EXPECT_EQ(adiak_get_nameval("c:vec:i16", &dtype, &val, &cat, &subcat), 0); + EXPECT_EQ(dtype->dtype, adiak_type_t::adiak_list); + EXPECT_EQ(cat, adiak_general); + EXPECT_EQ(adiak_num_subvals(dtype), 4); + EXPECT_EQ(adiak_get_subval(dtype, val, 0, &subtype, &subval), 0); + EXPECT_EQ(subtype->dtype, adiak_type_t::adiak_int); + EXPECT_EQ(subval.v_int, 256); + EXPECT_EQ(adiak_get_subval(dtype, val, 2, &subtype, &subval), 0); + EXPECT_EQ(subtype->dtype, adiak_type_t::adiak_int); + EXPECT_EQ(subval.v_int, -1024); + + EXPECT_EQ(adiak_get_nameval("c:vec:f32", &dtype, &val, &cat, &subcat), 0); + EXPECT_EQ(dtype->dtype, adiak_type_t::adiak_list); + EXPECT_EQ(cat, adiak_general); + EXPECT_EQ(adiak_num_subvals(dtype), 5); + EXPECT_EQ(adiak_get_subval(dtype, val, 0, &subtype, &subval), 0); + EXPECT_EQ(subtype->dtype, adiak_type_t::adiak_double); + EXPECT_FLOAT_EQ(subval.v_double, 1.5); + EXPECT_EQ(adiak_get_subval(dtype, val, 2, &subtype, &subval), 0); + EXPECT_EQ(subtype->dtype, adiak_type_t::adiak_double); + EXPECT_FLOAT_EQ(subval.v_double, 3.5); } @@ -283,6 +348,9 @@ static const int s_array[9] = { -1, 2, -3, 4, 5, 4, 3, 2, 1 }; static const char* s_string_a = "Static string A"; static const char* s_string_b = "Static string B"; +static const int64_t s_i64_array[4] = { -9876543210ll, 9876543210ll, 0ll, 424242424242ll }; +static const int16_t s_i16_array[4] = { -1024, 1, 2, 512 }; + /* Structs/tuples are super iffy! * There is no reliable way to determine their actual memory layout in adiak. * Using two 64-bit elements here which has a decent chance of working. @@ -299,6 +367,10 @@ TEST(AdiakApplicationAPI, C_ZeroCopyTypes) { /* zero-copy list of ints */ EXPECT_EQ(adiak_namevalue("cz:ref:vec:int", adiak_general, NULL, "&{%d}", s_array, 9), 0); + /* zero-copy list of i64s */ + EXPECT_EQ(adiak_namevalue("cz:ref:vec:i64", adiak_general, NULL, "&{%i64}", s_i64_array, 4), 0); + /* zero-copy list of i16s */ + EXPECT_EQ(adiak_namevalue("cz:ref:vec:i16", adiak_general, NULL, "&{%i16}", s_i16_array, 4), 0); /* a single zero-copy string */ EXPECT_EQ(adiak_namevalue("cz:ref:string", adiak_general, NULL, "&%s", s_string_a), 0); /* set of zero-copy strings (shallow-copies the list but not the strings)*/ @@ -334,6 +406,30 @@ TEST(AdiakApplicationAPI, C_ZeroCopyTypes) EXPECT_EQ(subtype->dtype, adiak_type_t::adiak_int); EXPECT_EQ(subval.v_int, -3); + EXPECT_EQ(adiak_get_nameval("cz:ref:vec:i64", &dtype, &val, &cat, &subcat), 0); + EXPECT_EQ(dtype->dtype, adiak_type_t::adiak_list); + EXPECT_EQ(dtype->is_reference, 1); + EXPECT_EQ(cat, adiak_general); + EXPECT_EQ(adiak_num_subvals(dtype), 4); + EXPECT_EQ(adiak_get_subval(dtype, val, 0, &subtype, &subval), 0); + EXPECT_EQ(subtype->dtype, adiak_type_t::adiak_longlong); + EXPECT_EQ(subval.v_longlong, -9876543210); + EXPECT_EQ(adiak_get_subval(dtype, val, 3, &subtype, &subval), 0); + EXPECT_EQ(subtype->dtype, adiak_type_t::adiak_longlong); + EXPECT_EQ(subval.v_longlong, 424242424242ll); + + EXPECT_EQ(adiak_get_nameval("cz:ref:vec:i16", &dtype, &val, &cat, &subcat), 0); + EXPECT_EQ(dtype->dtype, adiak_type_t::adiak_list); + EXPECT_EQ(dtype->is_reference, 1); + EXPECT_EQ(cat, adiak_general); + EXPECT_EQ(adiak_num_subvals(dtype), 4); + EXPECT_EQ(adiak_get_subval(dtype, val, 0, &subtype, &subval), 0); + EXPECT_EQ(subtype->dtype, adiak_type_t::adiak_int); + EXPECT_EQ(subval.v_int, -1024); + EXPECT_EQ(adiak_get_subval(dtype, val, 3, &subtype, &subval), 0); + EXPECT_EQ(subtype->dtype, adiak_type_t::adiak_int); + EXPECT_EQ(subval.v_int, 512); + EXPECT_EQ(adiak_get_nameval("cz:ref:string", &dtype, &val, nullptr, nullptr), 0); EXPECT_EQ(dtype->dtype, adiak_type_t::adiak_string); EXPECT_EQ(dtype->is_reference, 1); diff --git a/tests/test_zerocopy.c b/tests/test_zerocopy.c index 35bfad9..1f856ae 100644 --- a/tests/test_zerocopy.c +++ b/tests/test_zerocopy.c @@ -14,6 +14,10 @@ static const int array[9] = { -1, 2, -3, 4, 5, 4, 3, 2, 1 }; static const char* string_a = "One"; static const char* string_b = "Two"; +static float floaties[4] = { 1.5, 2.5, 3.5, 4.5 }; + +static unsigned char itsybitsyints[4] = { 2, 3, 4, 5 }; + /* Structs/tuples are super iffy! * There is no reliable way to determine their actual memory layout in adiak. * Using two 64-bit elements here which has a decent chance of working. @@ -32,6 +36,14 @@ void dowork() /* zero-copy list of ints */ result = adiak_namevalue("array_zerocopy", adiak_general, NULL, "&{%d}", array, 9); + if (result != 0) printf("return: %d\n\n", result); + + /* zero-copy list of u8s */ + result = adiak_namevalue("itsybitsyints_zerocopy", adiak_general, NULL, "&{%u8}", itsybitsyints, 4); + if (result != 0) printf("return: %d\n\n", result); + + /* zero-copy list of floats */ + result = adiak_namevalue("floaties_zerocopy", adiak_general, NULL, "&{%f32}", floaties, 4); if (result != 0) printf("return: %d\n\n", result); /* a single zero-copy string */ diff --git a/tests/testapp.c b/tests/testapp.c index 9ecf2fa..c688b91 100644 --- a/tests/testapp.c +++ b/tests/testapp.c @@ -15,6 +15,8 @@ void dowork(struct timeval start) struct timeval end; double gridarray[4] = { 4.5, 1.18, 0.24, 8.92 }; + float floaties[4] = { 1.5, 2.5, 3.5, 4.5 }; + unsigned char itsybitsyints[4] = { 2, 3, 4, 5 }; result = adiak_namevalue("compiler", adiak_general, NULL, "%s", "gcc@8.1.0"); if (result != 0) printf("return: %d\n\n", result); @@ -28,6 +30,12 @@ void dowork(struct timeval start) result = adiak_namevalue("countdown", adiak_general, NULL, "%lld", 9876543210); if (result != 0) printf("return: %d\n\n", result); + result = adiak_namevalue("floaties", adiak_general, NULL, "[%f32]", floaties, 4); + if (result != 0) printf("return: %d\n\n", result); + + result = adiak_namevalue("itsybitsyints", adiak_general, NULL, "[%u8]", itsybitsyints, 4); + if (result != 0) printf("return: %d\n\n", result); + result = adiak_walltime(); if (result != 0) printf("return: %d\n\n", result);