Skip to content
Merged
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
31 changes: 22 additions & 9 deletions abi/libasdf-x86_64.abi
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@
<elf-symbol name='asdf_file_error_common' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='asdf_file_error_oom' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='asdf_file_error_system' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='asdf_file_find' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='asdf_file_find_ex' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='asdf_file_log' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='asdf_find_iter_destroy' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='asdf_find_iter_init' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
Expand Down Expand Up @@ -2435,8 +2437,11 @@
</function-decl>
</abi-instr>
<abi-instr address-size='64' path='../src/file.c' language='LANG_C11'>
<typedef-decl name='asdf_depth_t' type-id='9da381c4' id='ef85e943'/>
<typedef-decl name='asdf_value_pred_t' type-id='c988a833' id='55806fb9'/>
<pointer-type-def type-id='241167d5' size-in-bits='64' id='c666b66d'/>
<pointer-type-def type-id='2f407acc' size-in-bits='64' id='48817812'/>
<pointer-type-def type-id='b5b387ef' size-in-bits='64' id='c988a833'/>
<pointer-type-def type-id='c894953d' size-in-bits='64' id='d8e6b335'/>
<pointer-type-def type-id='80f4b756' size-in-bits='64' id='7d3cd834'/>
<pointer-type-def type-id='a0eb0f08' size-in-bits='64' id='7408d286'/>
Expand All @@ -2446,8 +2451,6 @@
<pointer-type-def type-id='ee31ee44' size-in-bits='64' id='256d5229'/>
<pointer-type-def type-id='149c6638' size-in-bits='64' id='8a121f49'/>
<pointer-type-def type-id='8f92235e' size-in-bits='64' id='90421557'/>
<pointer-type-def type-id='b2dd3d8e' size-in-bits='64' id='fc7bf74e'/>
<class-decl name='fy_node' is-struct='yes' visibility='default' is-declaration-only='yes' id='b2dd3d8e'/>
<function-decl name='asdf_open_file_ex' mangled-name='asdf_open_file_ex' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='asdf_open_file_ex'>
<parameter type-id='80f4b756' name='filename'/>
<parameter type-id='80f4b756' name='mode'/>
Expand Down Expand Up @@ -2814,6 +2817,23 @@
<parameter type-id='2f407acc' name='sequence'/>
<return type-id='88078e53'/>
</function-decl>
<function-decl name='asdf_file_find' mangled-name='asdf_file_find' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='asdf_file_find'>
<parameter type-id='b14ada4d' name='file'/>
<parameter type-id='55806fb9' name='pred'/>
<return type-id='4c28b278'/>
</function-decl>
<function-decl name='asdf_file_find_ex' mangled-name='asdf_file_find_ex' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='asdf_file_find_ex'>
<parameter type-id='b14ada4d' name='file'/>
<parameter type-id='55806fb9' name='pred'/>
<parameter type-id='c894953d' name='depth_first'/>
<parameter type-id='55806fb9' name='descend_pred'/>
<parameter type-id='ef85e943' name='max_depth'/>
<return type-id='4c28b278'/>
</function-decl>
<function-type size-in-bits='64' id='b5b387ef'>
<parameter type-id='4c28b278'/>
<return type-id='c894953d'/>
</function-type>
</abi-instr>
<abi-instr address-size='64' path='../src/info.c' language='LANG_C11'>
<qualified-type-def type-id='cd88a909' const='yes' id='5ab04299'/>
Expand Down Expand Up @@ -2957,11 +2977,9 @@
</data-member>
</class-decl>
<typedef-decl name='asdf_container_iter_t' type-id='86b8634d' id='4d433ebe'/>
<typedef-decl name='asdf_depth_t' type-id='9da381c4' id='ef85e943'/>
<typedef-decl name='asdf_find_iter_t' type-id='031f26e1' id='a42f9c70'/>
<typedef-decl name='asdf_mapping_iter_t' type-id='b392b722' id='7738f409'/>
<typedef-decl name='asdf_sequence_iter_t' type-id='da91e0b3' id='67aa79fa'/>
<typedef-decl name='asdf_value_pred_t' type-id='c988a833' id='55806fb9'/>
<pointer-type-def type-id='4d433ebe' size-in-bits='64' id='35494c2d'/>
<pointer-type-def type-id='35494c2d' size-in-bits='64' id='e962af05'/>
<pointer-type-def type-id='a42f9c70' size-in-bits='64' id='601313a9'/>
Expand All @@ -2970,7 +2988,6 @@
<pointer-type-def type-id='48fb8db0' size-in-bits='64' id='7dffe96e'/>
<pointer-type-def type-id='67aa79fa' size-in-bits='64' id='5da8d9c3'/>
<pointer-type-def type-id='5da8d9c3' size-in-bits='64' id='59cf09cb'/>
<pointer-type-def type-id='b5b387ef' size-in-bits='64' id='c988a833'/>
<qualified-type-def type-id='c894953d' const='yes' id='4ab96a04'/>
<pointer-type-def type-id='4ab96a04' size-in-bits='64' id='cfe2266a'/>
<qualified-type-def type-id='a0eb0f08' const='yes' id='c80c3491'/>
Expand Down Expand Up @@ -3710,10 +3727,6 @@
<parameter type-id='55806fb9' name='pred'/>
<return type-id='4c28b278'/>
</function-decl>
<function-type size-in-bits='64' id='b5b387ef'>
<parameter type-id='4c28b278'/>
<return type-id='c894953d'/>
</function-type>
</abi-instr>
<abi-instr address-size='64' path='../src/version.c' language='LANG_C11'>
<function-decl name='asdf_version_parse' mangled-name='asdf_version_parse' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='asdf_version_parse'>
Expand Down
7 changes: 7 additions & 0 deletions changes/246.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Added ``asdf_file_find`` and ``asdf_file_find_ex`` functions, corresponding to
``asdf_value_find`` and ``asdf_value_find_ex``, the only difference being that
they take the ``asdf_file_t *`` as their first argument as a shortcut for
searching from the root of the tree.

Also adds convenience macros ``asdf_find`` and ``asdf_find_ex`` which are
generic in the first argument (can be either a file or a value).
16 changes: 10 additions & 6 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,16 @@ AC_INIT(
#
# History:
#
# - 0:0:0 (0.1.0): initial default
# - 1:0:1 (0.2.0): added asdf_free(). Also changed max_depth from ssize_t
# to asdf_depth_t (int64_t): identical on LP64, but wider
# on ILP32, where it is an interface change rather than an
# addition. age was deliberately NOT reset, knowingly
# breaking 32-bit targets, which are not actively supported.
# - 0:0:0 (0.1.0): Initial default
# - 1:0:1 (0.2.0): Added asdf_free().
#
# Changed max_depth from ssize_t to asdf_depth_t (int64_t):
# identical on LP64, but wider on ILP32, where it is an
# interface change rather than an addition. age was
# deliberately NOT reset, knowingly breaking 32-bit
# targets, which are not actively supported.
#
# Added asdf_file_find and asdf_file_find_ex.
LIBASDF_VERSION_INFO=1:0:1
AC_SUBST([LIBASDF_VERSION_INFO])

Expand Down
6 changes: 2 additions & 4 deletions docs/usage/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,7 @@ the ASDF tree, as well as extract block data. Inline comments provide further e
asdf_meta_destroy(meta);

// Find the first ndarray in the file, if any
asdf_value_t *root = asdf_get_value(file, "");
asdf_value_t *value = asdf_value_find(root, asdf_value_is_ndarray);
asdf_value_t *value = asdf_find(file, asdf_value_is_ndarray);

if (!value) {
fprintf(stderr, "no ndarray found in the file\n");
Expand All @@ -96,9 +95,8 @@ the ASDF tree, as well as extract block data. Inline comments provide further e
printf("Using ndarray at: %s\n", asdf_value_path(value));
printf("Number of data dimensions: %d\n", ndarray->ndim);

// The generic value wrappers are no longer needed and should be freed.
// The generic value wrapper is no longer needed and should be freed.
asdf_value_destroy(value);
asdf_value_destroy(root);

// Get just a raw pointer to the ndarray data block (if uncompressed).
// Optionally returns the size in bytes as well
Expand Down
8 changes: 3 additions & 5 deletions docs/usage/ndarrays.rst
Original file line number Diff line number Diff line change
Expand Up @@ -93,21 +93,19 @@ you are done:
asdf_ndarray_destroy(array);

If you do not know the path in advance you can search the tree for the first
ndarray with `asdf_value_find` and the ``asdf_value_is_ndarray`` predicate, then
cast the matching value with `asdf_value_as_ndarray`:
ndarray with `asdf_find` and the ``asdf_value_is_ndarray`` predicate, then cast
the matching value with `asdf_value_as_ndarray`:

.. code:: c

asdf_value_t *root = asdf_get_value(file, "");
asdf_value_t *found = asdf_value_find(root, asdf_value_is_ndarray);
asdf_value_t *found = asdf_find(file, asdf_value_is_ndarray);

asdf_ndarray_t *array = NULL;
if (found && asdf_value_as_ndarray(found, &array) == ASDF_VALUE_OK) {
/* ... */
}

asdf_value_destroy(found);
asdf_value_destroy(root);

See :ref:`values` for more on generic value handles and tree traversal.

Expand Down
100 changes: 100 additions & 0 deletions include/asdf/file.h
Original file line number Diff line number Diff line change
Expand Up @@ -947,6 +947,106 @@ asdf_set_mapping(asdf_file_t *file, const char *path, asdf_mapping_t *mapping);
ASDF_EXPORT asdf_value_err_t
asdf_set_sequence(asdf_file_t *file, const char *path, asdf_sequence_t *sequence);


/**
* .. _file-traversal:
*
* Tree traversal
* --------------
*/

// clang-format off

/**
* Traverse the tree breadth-first starting from ``root`` and return the first
* value matching ``pred``
*
* The caller owns the returned `asdf_value_t *` and must eventually destroy it
* with `asdf_value_destroy`. Returns ``NULL`` if no matching value was found.
*
* This is a macro that can take either an `asdf_file_t *` or `asdf_value_t *`
* as the root from which to traverse. In the former case, this is equivalent
* to starting the traversal from the root of the tree (via `asdf_file_find`).
*
* :param root: `asdf_value_t *` handle for the root node to search from or
* an `asdf_file_t *`.
* :param pred: A predicate function to match the value to return; see
* `asdf_value_pred_t`
* :return: The first matching `asdf_value_t *`, or ``NULL`` if not found
*/
#define asdf_find(root, pred) /* NOLINT(readability-identifier-naming) */ \
_Generic( \
(root), \
asdf_file_t *: asdf_file_find, \
asdf_value_t *: asdf_value_find)(root, pred)

/**
* Extended version of `asdf_find` with additional traversal options
*
* Like `asdf_find` but allows controlling traversal order, which
* container types to descend into, and the maximum search depth.
*
* :param root: `asdf_value_t *` handle for the root node to search from
* or an `asdf_file_t *`
* :param pred: A predicate function to match the value to return; see
* `asdf_value_pred_t`
* :param depth_first: If ``true`` descend the tree in depth-first order;
* otherwise the tree is traversed breadth-first
* :param descend_pred: Optional predicate (``NULL`` means descend into all
* containers) controlling which containers are descended into
* :param max_depth: Maximum depth to descend; ``-1`` means no limit
* :return: The first matching `asdf_value_t *`, or ``NULL`` if not found
*/
#define asdf_find_ex(root, pred, depth_first, descend_pred, max_depth) /* NOLINT(readability-identifier-naming) */ \
_Generic( \
(root), \
asdf_file_t *: asdf_file_find_ex, \
asdf_value_t *: asdf_value_find_ex)(root, pred, depth_first, descend_pred, max_depth)

// clang-format on

/**
* Traverse the tree breadth-first starting from root of the given file's tree
* and return the first value matching ``pred``
*
* This is a shorthand for calling `asdf_value_find` starting from the root
* of the tree, equivalent to:
*
* .. code:: c
*
* asdf_value_t *root = asdf_get_value(file, "");
* asdf_value_t *found = asdf_value_find(root, pred);
*
* :param root: `asdf_file_t *` handle for the file to search
* :param pred: A predicate function to match the value to return; see
* `asdf_value_pred_t`
* :return: The first matching `asdf_value_t *`, or ``NULL`` if not found
*/
ASDF_EXPORT asdf_value_t *asdf_file_find(asdf_file_t *file, asdf_value_pred_t pred);

/**
* Extended version of `asdf_file_find` with additional traversal options
*
* This is just the file-level companion to `asdf_file_find`, similarly to
* `asdf_value_find_ex`.
*
* :param root: `asdf_file_t *` handle for the file to search
* :param pred: A predicate function to match the value to return; see
* `asdf_value_pred_t`
* :param depth_first: If ``true`` descend the tree in depth-first order;
* otherwise the tree is traversed breadth-first
* :param descend_pred: Optional predicate (``NULL`` means descend into all
* containers) controlling which containers are descended into
* :param max_depth: Maximum depth to descend; ``-1`` means no limit
* :return: The first matching `asdf_value_t *`, or ``NULL`` if not found
*/
ASDF_EXPORT asdf_value_t *asdf_file_find_ex(
asdf_file_t *file,
asdf_value_pred_t pred,
bool depth_first,
asdf_value_pred_t descend_pred,
asdf_depth_t max_depth);

ASDF_END_DECLS

#endif /* ASDF_FILE_H */
25 changes: 22 additions & 3 deletions src/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>

#include <libfyaml.h>

#include "block.h"
#include "context.h"
#include "core/asdf.h"
Expand Down Expand Up @@ -1019,3 +1016,25 @@ asdf_value_err_t asdf_set_sequence(asdf_file_t *file, const char *path, asdf_seq
asdf_sequence_destroy(sequence);
return err;
}


asdf_value_t *asdf_file_find(asdf_file_t *file, asdf_value_pred_t pred) {
return asdf_file_find_ex(file, pred, false, NULL, -1);
}


asdf_value_t *asdf_file_find_ex(
asdf_file_t *file,
asdf_value_pred_t pred,
bool depth_first,
asdf_value_pred_t descend_pred,
asdf_depth_t max_depth) {
asdf_value_t *root = asdf_get_value(file, "");

if (UNLIKELY(!root))
return NULL;

asdf_value_t *found = asdf_value_find_ex(root, pred, depth_first, descend_pred, max_depth);
asdf_value_destroy(root);
return found;
}
2 changes: 0 additions & 2 deletions src/file.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

#include <stdbool.h>

#include <libfyaml.h>

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
Expand Down
37 changes: 33 additions & 4 deletions tests/test-file.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef _GNU_SOURCE
#define _GNU_SOURCE /* for memmem */
#endif
#include <errno.h>
#include <float.h>
#include <stdbool.h>
#include <stdint.h>
Expand All @@ -9,9 +10,7 @@
#include <string.h>
#include <unistd.h>

#include <stc/cstr.h>

#include <errno.h>
#include "stc/cstr.h"

#include "asdf/emitter.h"
#include "asdf/error.h"
Expand Down Expand Up @@ -869,6 +868,35 @@ MU_TEST(test_asdf_free) {
}


static bool value_find_pred_b(asdf_value_t *value) {
const char *str = NULL;
asdf_value_err_t err = asdf_value_as_string0(value, &str);

if (ASDF_VALUE_OK != err)
return false;

return strcmp(str, "b") == 0;
}


MU_TEST(test_asdf_file_find) {
const char *filename = get_fixture_file_path("nested.asdf");
asdf_file_t *file = asdf_open(filename, "r");
assert_not_null(file);
asdf_value_t *val = asdf_file_find(file, value_find_pred_b);
assert_not_null(val);
const char *str = NULL;
// BFS should find the value "b" at the top-level first
assert_string_equal(asdf_value_path(val), "/b");
asdf_value_err_t err = asdf_value_as_string0(val, &str);
assert_int(err, ==, ASDF_VALUE_OK);
assert_string_equal(str, "b");
asdf_value_destroy(val);
asdf_close(file);
return MUNIT_OK;
}


MU_TEST_SUITE(
file,
MU_RUN_TEST(test_asdf_open_file),
Expand Down Expand Up @@ -902,7 +930,8 @@ MU_TEST_SUITE(
MU_RUN_TEST(write_custom_tag_handle),
MU_RUN_TEST(write_to_nonexistent_file),
MU_RUN_TEST(test_asdf_set_value_double_free),
MU_RUN_TEST(test_asdf_free)
MU_RUN_TEST(test_asdf_free),
MU_RUN_TEST(test_asdf_file_find)
);


Expand Down