/**
 * @file   tiledb.h
 *
 * @section LICENSE
 *
 * The MIT License
 *
 * @copyright Copyright (c) 2017-2021 TileDB, Inc.
 * @copyright Copyright (c) 2016 MIT and Intel Corporation
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * @section DESCRIPTION
 *
 * This file declares the C API for TileDB.
 */

#ifndef TILEDB_H
#define TILEDB_H

#include "tiledb_version.h"

// Generated by CMake; defines TILEDB_EXPORT (etc) macros.
#include "tiledb_export.h"

#include <stdint.h>
#include <stdio.h>

/* ********************************* */
/*               MACROS              */
/* ********************************* */

#ifdef __cplusplus
extern "C" {
#endif

/* ****************************** */
/*          TILEDB ENUMS          */
/* ****************************** */

/** TileDB object type. */
typedef enum {
/** Helper macro for defining object type enums. */
#define TILEDB_OBJECT_TYPE_ENUM(id) TILEDB_##id
#include "tiledb_enum.h"
#undef TILEDB_OBJECT_TYPE_ENUM
} tiledb_object_t;

/** TileDB query type. */
typedef enum {
/** Helper macro for defining query type enums. */
#define TILEDB_QUERY_TYPE_ENUM(id) TILEDB_##id
#include "tiledb_enum.h"
#undef TILEDB_QUERY_TYPE_ENUM
} tiledb_query_type_t;

/** Query status. */
typedef enum {
/** Helper macro for defining query status enums. */
#define TILEDB_QUERY_STATUS_ENUM(id) TILEDB_##id
#include "tiledb_enum.h"
#undef TILEDB_QUERY_STATUS_ENUM
} tiledb_query_status_t;

/** Query condition operator. */
typedef enum {
/** Helper macro for defining query condition operator enums. */
#define TILEDB_QUERY_CONDITION_OP_ENUM(id) TILEDB_##id
#include "tiledb_enum.h"
#undef TILEDB_QUERY_CONDITION_OP_ENUM
} tiledb_query_condition_op_t;

/** Query condition combination operator. */
typedef enum {
/** Helper macro for defining query condition combination operator enums. */
#define TILEDB_QUERY_CONDITION_COMBINATION_OP_ENUM(id) TILEDB_##id
#include "tiledb_enum.h"
#undef TILEDB_QUERY_CONDITION_COMBINATION_OP_ENUM
} tiledb_query_condition_combination_op_t;

/** Filesystem type. */
typedef enum {
/** Helper macro for defining filesystem enums. */
#define TILEDB_FILESYSTEM_ENUM(id) TILEDB_##id
#include "tiledb_enum.h"
#undef TILEDB_FILESYSTEM_ENUM
} tiledb_filesystem_t;

/** TileDB datatype. */
typedef enum {
/** Helper macro for defining datatype enums. */
#define TILEDB_DATATYPE_ENUM(id) TILEDB_##id
#include "tiledb_enum.h"
#undef TILEDB_DATATYPE_ENUM
} tiledb_datatype_t;

/** Array type. */
typedef enum {
/** Helper macro for defining array type enums. */
#define TILEDB_ARRAY_TYPE_ENUM(id) TILEDB_##id
#include "tiledb_enum.h"
#undef TILEDB_ARRAY_TYPE_ENUM
} tiledb_array_type_t;

/** Tile or cell layout. */
typedef enum {
/** Helper macro for defining layout type enums. */
#define TILEDB_LAYOUT_ENUM(id) TILEDB_##id
#include "tiledb_enum.h"
#undef TILEDB_LAYOUT_ENUM
} tiledb_layout_t;

/** Filter type. */
typedef enum {
/** Helper macro for defining filter type enums. */
#define TILEDB_FILTER_TYPE_ENUM(id) TILEDB_##id
#include "tiledb_enum.h"
#undef TILEDB_FILTER_TYPE_ENUM
} tiledb_filter_type_t;

/** Filter option. */
typedef enum {
/** Helper macro for defining filter option enums. */
#define TILEDB_FILTER_OPTION_ENUM(id) TILEDB_##id
#include "tiledb_enum.h"
#undef TILEDB_FILTER_OPTION_ENUM
} tiledb_filter_option_t;

/** Encryption type. */
typedef enum {
/** Helper macro for defining encryption enums. */
#define TILEDB_ENCRYPTION_TYPE_ENUM(id) TILEDB_##id
#include "tiledb_enum.h"
#undef TILEDB_ENCRYPTION_TYPE_ENUM
} tiledb_encryption_type_t;

/** Walk traversal order. */
typedef enum {
/** Helper macro for defining walk order enums. */
#define TILEDB_WALK_ORDER_ENUM(id) TILEDB_##id
#include "tiledb_enum.h"
#undef TILEDB_WALK_ORDER_ENUM
} tiledb_walk_order_t;

/** VFS mode. */
typedef enum {
/** Helper macro for defining VFS mode enums. */
#define TILEDB_VFS_MODE_ENUM(id) TILEDB_##id
#include "tiledb_enum.h"
#undef TILEDB_VFS_MODE_ENUM
} tiledb_vfs_mode_t;

/* ****************************** */
/*       ENUMS TO/FROM STR        */
/* ****************************** */

/**
 * Returns a string representation of the given query type.
 *
 * @param query_type Query type
 * @param str Set to point to a constant string representation of the query type
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_query_type_to_str(tiledb_query_type_t query_type, const char** str);

/**
 * Parses a query type from the given string.
 *
 * @param str String representation to parse
 * @param query_type Set to the parsed query type
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_query_type_from_str(const char* str, tiledb_query_type_t* query_type);

/**
 * Returns a string representation of the given object type.
 *
 * @param object_type Object type
 * @param str Set to point to a constant string representation of the object
 * type
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_object_type_to_str(tiledb_object_t object_type, const char** str);

/**
 * Parses a object type from the given string.
 *
 * @param str String representation to parse
 * @param object_type Set to the parsed object type
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_object_type_from_str(const char* str, tiledb_object_t* object_type);

/**
 * Returns a string representation of the given filesystem.
 *
 * @param filesystem Filesystem
 * @param str Set to point to a constant string representation of the filesystem
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_filesystem_to_str(tiledb_filesystem_t filesystem, const char** str);

/**
 * Parses a filesystem from the given string.
 *
 * @param str String representation to parse
 * @param filesystem Set to the parsed filesystem
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_filesystem_from_str(const char* str, tiledb_filesystem_t* filesystem);

/**
 * Returns a string representation of the given datatype.
 *
 * @param datatype Datatype
 * @param str Set to point to a constant string representation of the datatype
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_datatype_to_str(tiledb_datatype_t datatype, const char** str);

/**
 * Parses a datatype from the given string.
 *
 * @param str String representation to parse
 * @param datatype Set to the parsed datatype
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_datatype_from_str(const char* str, tiledb_datatype_t* datatype);

/**
 * Returns a string representation of the given array type.
 *
 * @param array_type Array type
 * @param str Set to point to a constant string representation of the array type
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_array_type_to_str(tiledb_array_type_t array_type, const char** str);

/**
 * Parses a array type from the given string.
 *
 * @param str String representation to parse
 * @param array_type Set to the parsed array type
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_array_type_from_str(const char* str, tiledb_array_type_t* array_type);

/**
 * Returns a string representation of the given layout.
 *
 * @param layout Layout
 * @param str Set to point to a constant string representation of the layout
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_layout_to_str(tiledb_layout_t layout, const char** str);

/**
 * Parses a layout from the given string.
 *
 * @param str String representation to parse
 * @param layout Set to the parsed layout
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_layout_from_str(const char* str, tiledb_layout_t* layout);

/**
 * Returns a string representation of the given filter type.
 *
 * @param filter_type Filter type
 * @param str Set to point to a constant string representation of the filter
 * type
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_filter_type_to_str(tiledb_filter_type_t filter_type, const char** str);

/**
 * Parses a filter type from the given string.
 *
 * @param str String representation to parse
 * @param filter_type Set to the parsed filter type
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_filter_type_from_str(const char* str, tiledb_filter_type_t* filter_type);

/**
 * Returns a string representation of the given filter option.
 *
 * @param filter_option Filter option
 * @param str Set to point to a constant string representation of the filter
 * option
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_filter_option_to_str(
    tiledb_filter_option_t filter_option, const char** str);

/**
 * Parses a filter option from the given string.
 *
 * @param str String representation to parse
 * @param filter_option Set to the parsed filter option
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_filter_option_from_str(
    const char* str, tiledb_filter_option_t* filter_option);

/**
 * Returns a string representation of the given encryption type.
 *
 * @param encryption_type Encryption type
 * @param str Set to point to a constant string representation of the encryption
 * type
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_encryption_type_to_str(
    tiledb_encryption_type_t encryption_type, const char** str);

/**
 * Parses a encryption type from the given string.
 *
 * @param str String representation to parse
 * @param encryption_type Set to the parsed encryption type
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_encryption_type_from_str(
    const char* str, tiledb_encryption_type_t* encryption_type);

/**
 * Returns a string representation of the given query status.
 *
 * @param query_status Query status
 * @param str Set to point to a constant string representation of the query
 * status
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_query_status_to_str(
    tiledb_query_status_t query_status, const char** str);

/**
 * Parses a query status from the given string.
 *
 * @param str String representation to parse
 * @param query_status Set to the parsed query status
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_query_status_from_str(
    const char* str, tiledb_query_status_t* query_status);

/**
 * Returns a string representation of the given walk order.
 *
 * @param walk_order Walk order
 * @param str Set to point to a constant string representation of the walk order
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_walk_order_to_str(tiledb_walk_order_t walk_order, const char** str);

/**
 * Parses a walk order from the given string.
 *
 * @param str String representation to parse
 * @param walk_order Set to the parsed walk order
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_walk_order_from_str(const char* str, tiledb_walk_order_t* walk_order);

/**
 * Returns a string representation of the given VFS mode.
 *
 * @param vfs_mode VFS mode
 * @param str Set to point to a constant string representation of the VFS mode
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_vfs_mode_to_str(tiledb_vfs_mode_t vfs_mode, const char** str);

/**
 * Parses a VFS mode from the given string.
 *
 * @param str String representation to parse
 * @param vfs_mode Set to the parsed VFS mode
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_vfs_mode_from_str(const char* str, tiledb_vfs_mode_t* vfs_mode);

/* ****************************** */
/*            CONSTANTS           */
/* ****************************** */

/**
 * @name Return codes
 */
/**@{*/
/** Success */
#define TILEDB_OK 0
/** General error */
#define TILEDB_ERR (-1)
/** Out of memory */
#define TILEDB_OOM (-2)
/**@}*/

/**
 * Returns a special name indicating the coordinates attribute.
 *
 * The coordinate buffer has been deprecated. Set the coordinates for
 * each individual dimension with the `set_buffer` API. Consult the current
 * documentation for more information.
 */
TILEDB_DEPRECATED_EXPORT const char* tiledb_coords(void);

/** Returns a special value indicating a variable number of elements. */
TILEDB_EXPORT uint32_t tiledb_var_num(void);

/** Returns the maximum path length on the current platform. */
TILEDB_EXPORT uint32_t tiledb_max_path(void);

/** Returns the input datatype size. */
TILEDB_EXPORT uint64_t tiledb_datatype_size(tiledb_datatype_t type);

/**
 * Returns the size (in bytes) of an offset (used in variable-sized
 * attributes).
 */
TILEDB_EXPORT uint64_t tiledb_offset_size(void);

/** Returns the current time in milliseconds. */
TILEDB_EXPORT uint64_t tiledb_timestamp_now_ms(void);

/**
 * @name Constants wrapping special functions
 */
/**@{*/
/** A special name indicating the coordinates attribute. */
#define TILEDB_COORDS tiledb_coords()
/** A special value indicating a variable number of elements. */
#define TILEDB_VAR_NUM tiledb_var_num()
/** The maximum path length on the current platform. */
#define TILEDB_MAX_PATH tiledb_max_path()
/** The size (in bytes) of an offset (used in variable-sized attributes). */
#define TILEDB_OFFSET_SIZE tiledb_offset_size()
/** The current time in milliseconds. */
#define TILEDB_TIMESTAMP_NOW_MS tiledb_timestamp_now_ms()
/**@}*/

/* ****************************** */
/*            VERSION             */
/* ****************************** */

/**
 *  Retrieves the version of the TileDB library currently being used.
 *
 *  @param major Will store the major version number.
 *  @param minor Will store the minor version number.
 *  @param rev Will store the revision (patch) number.
 */
TILEDB_EXPORT void tiledb_version(int32_t* major, int32_t* minor, int32_t* rev);

/* ********************************* */
/*           TILEDB TYPES            */
/* ********************************* */

/** An array object. */
typedef struct tiledb_array_t tiledb_array_t;

/** A generic buffer object. */
typedef struct tiledb_buffer_t tiledb_buffer_t;

/** A generic buffer list object. */
typedef struct tiledb_buffer_list_t tiledb_buffer_list_t;

/** A config object. */
typedef struct tiledb_config_t tiledb_config_t;

/** A config iterator. */
typedef struct tiledb_config_iter_t tiledb_config_iter_t;

/** A TileDB context. */
typedef struct tiledb_ctx_t tiledb_ctx_t;

/** A TileDB error. **/
typedef struct tiledb_error_t tiledb_error_t;

/** A TileDB attribute. */
typedef struct tiledb_attribute_t tiledb_attribute_t;

/** A TileDB array schema. */
typedef struct tiledb_array_schema_t tiledb_array_schema_t;

/** A TileDB dimension. */
typedef struct tiledb_dimension_t tiledb_dimension_t;

/** A TileDB domain. */
typedef struct tiledb_domain_t tiledb_domain_t;

/** A TileDB filter. */
typedef struct tiledb_filter_t tiledb_filter_t;

/** A TileDB filter list. */
typedef struct tiledb_filter_list_t tiledb_filter_list_t;

/** A TileDB query. */
typedef struct tiledb_query_t tiledb_query_t;

/** A TileDB query condition object. */
typedef struct tiledb_query_condition_t tiledb_query_condition_t;

/** A virtual filesystem object. */
typedef struct tiledb_vfs_t tiledb_vfs_t;

/** A virtual filesystem file handle. */
typedef struct tiledb_vfs_fh_t tiledb_vfs_fh_t;

/** A fragment info object. */
typedef struct tiledb_fragment_info_t tiledb_fragment_info_t;

/* ********************************* */
/*              ERROR                */
/* ********************************* */

/**
 * Returns the error message associated with a TileDB error object.
 *
 * **Example:**
 *
 * The following shows how to get the last error from a TileDB context.
 *
 * @code{.c}
 * tiledb_error_t* err = NULL;
 * tiledb_ctx_get_last_error(ctx, &err);
 * const char* msg;
 * tiledb_error_message(err, &msg);
 * printf("%s\n", msg);
 * @endcode
 *
 * @param err A TileDB error object.
 * @param errmsg A constant pointer to the error message.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_error_message(tiledb_error_t* err, const char** errmsg);

/**
 * Frees the resources associated with a TileDB error object.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_error_t* err = NULL;
 * tiledb_ctx_get_last_error(ctx, &err);
 * const char* msg;
 * tiledb_error_message(err, &msg);
 * printf("%s\n", msg);
 * tiledb_error_free(&err);
 * @endcode
 *
 * @param err The TileDB error object.
 */
TILEDB_EXPORT void tiledb_error_free(tiledb_error_t** err);

/* ********************************* */
/*              BUFFER               */
/* ********************************* */

/**
 * Creates an empty buffer object.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_buffer_t* buffer;
 * tiledb_buffer_alloc(ctx, &buffer);
 * @endcode
 *
 * @param ctx TileDB context
 * @param buffer The buffer to be created
 * @return `TILEDB_OK` for success and `TILEDB_OOM` or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_buffer_alloc(tiledb_ctx_t* ctx, tiledb_buffer_t** buffer);

/**
 * Destroys a TileDB buffer, freeing associated memory.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_buffer_t* buffer;
 * tiledb_buffer_alloc(ctx, &buffer);
 * tiledb_buffer_free(&buffer);
 * @endcode
 *
 * @param buffer The buffer to be destroyed.
 */
TILEDB_EXPORT void tiledb_buffer_free(tiledb_buffer_t** buffer);

/**
 * Sets a datatype for the given buffer. The default datatype is `TILEDB_UINT8`.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_buffer_t* buffer;
 * tiledb_buffer_alloc(ctx, &buffer);
 * tiledb_buffer_set_type(ctx, buffer, TILEDB_INT32);
 * @endcode
 *
 * @param ctx TileDB context
 * @param buffer TileDB buffer instance
 * @param datatype The datatype to set on the buffer.
 * @return `TILEDB_OK` for success or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_buffer_set_type(
    tiledb_ctx_t* ctx, tiledb_buffer_t* buffer, tiledb_datatype_t datatype);

/**
 * Gets the datatype from the given buffer.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_datatype_t type;
 * tiledb_buffer_get_type(ctx, buffer, &type);
 * @endcode
 *
 * @param ctx TileDB context
 * @param buffer TileDB buffer instance
 * @param datatype Set to the datatype of the buffer.
 * @return `TILEDB_OK` for success or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_buffer_get_type(
    tiledb_ctx_t* ctx,
    const tiledb_buffer_t* buffer,
    tiledb_datatype_t* datatype);

/**
 * Gets a pointer to the current allocation and the current number of bytes in
 * the specified buffer object.
 *
 * @note For string buffers allocated by TileDB, the number of bytes includes
 * the terminating NULL byte.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_buffer_t* buffer;
 * tiledb_buffer_alloc(ctx, &buffer);
 * void* data;
 * uint64_t num_bytes;
 * tiledb_buffer_get_data(ctx, buffer, &data, num_bytes);
 * // data == NULL and num_bytes == 0 because the buffer is currently empty.
 * tiledb_buffer_free(&buffer);
 * @endcode
 *
 * @param ctx TileDB context
 * @param buffer TileDB buffer instance
 * @param data The pointer to the data to be retrieved.
 * @param num_bytes Set to the number of bytes in the buffer.
 * @return `TILEDB_OK` for success or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_buffer_get_data(
    tiledb_ctx_t* ctx,
    const tiledb_buffer_t* buffer,
    void** data,
    uint64_t* num_bytes);

/**
 * Sets (wraps) a pre-allocated region of memory with the given buffer object.
 * This does not perform a copy.
 *
 * @note The TileDB buffer object does not take ownership of the allocation
 * set with this function. That means the call to `tiledb_buffer_free` will not
 * free a user allocation set via `tiledb_buffer_set_buffer`.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_buffer_t* buffer;
 * tiledb_buffer_alloc(ctx, &buffer);
 *
 * void* my_data = malloc(100);
 * tiledb_buffer_set_data(ctx, buffer, my_data, 100);
 *
 * void* data;
 * uint64_t num_bytes;
 * tiledb_buffer_get_data(ctx, buffer, &data, num_bytes);
 * assert(data == my_data);
 * assert(num_bytes == 100);
 *
 * tiledb_buffer_free(&buffer);
 * free(my_data);
 * @endcode
 *
 * @param ctx TileDB context
 * @param buffer TileDB buffer instance
 * @param data Pre-allocated region of memory to wrap with this buffer.
 * @param size Size (in bytes) of the region pointed to by data.
 * @return `TILEDB_OK` for success or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_buffer_set_data(
    tiledb_ctx_t* ctx, tiledb_buffer_t* buffer, void* data, uint64_t size);

/* ********************************* */
/*            BUFFER LIST            */
/* ********************************* */

/**
 * Creates an empty buffer list object.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_buffer_list_t* buffer_list;
 * tiledb_buffer_list_alloc(ctx, &buffer_list);
 * @endcode
 *
 * @param ctx TileDB context
 * @param buffer_list The buffer list to be created
 * @return `TILEDB_OK` for success and `TILEDB_OOM` or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_buffer_list_alloc(tiledb_ctx_t* ctx, tiledb_buffer_list_t** buffer_list);

/**
 * Destroys a TileDB buffer list, freeing associated memory.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_buffer_t* buffer_list;
 * tiledb_buffer_list_alloc(ctx, &buffer_list);
 * tiledb_buffer_list_free(&buffer_list);
 * @endcode
 *
 * @param buffer_list The buffer list to be destroyed.
 */
TILEDB_EXPORT void tiledb_buffer_list_free(tiledb_buffer_list_t** buffer_list);

/**
 * Gets the number of buffers in the buffer list.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_buffer_list_t* buffer_list;
 * tiledb_buffer_list_alloc(ctx, &buffer_list);
 * uint64_t num_buffers;
 * tiledb_buffer_list_get_num_buffers(ctx, buffer_list, &num_buffers);
 * // num_buffers == 0 because the list is empty.
 * @endcode
 *
 * @param ctx TileDB context.
 * @param buffer_list The buffer list.
 * @param num_buffers Set to the number of buffers in the buffer list.
 * @return `TILEDB_OK` for success or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_buffer_list_get_num_buffers(
    tiledb_ctx_t* ctx,
    const tiledb_buffer_list_t* buffer_list,
    uint64_t* num_buffers);

/**
 * Gets the buffer at the given index in the buffer list. The returned buffer
 * object is simply a pointer to memory managed by the underlying buffer
 * list, meaning this function does not perform a copy.
 *
 * It is the caller's responsibility to free the returned buffer with
 * `tiledb_buffer_free`. Since the returned buffer object does not "own" the
 * underlying allocation, the underlying allocation is not freed when freeing it
 * with `tiledb_buffer_free`.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_buffer_list_t* buffer_list;
 * // Create and populate the buffer_list
 *
 * // Get the buffer at index 0.
 * tiledb_buffer_t *buff0;
 * tiledb_buffer_list_get_buffer(ctx, buffer_list, 0, &buff0);
 *
 * // Always free the returned buffer object
 * tiledb_buffer_free(&buff0);
 * tiledb_buffer_list_free(&buffer_list);
 * @endcode
 *
 * @param ctx TileDB context.
 * @param buffer_list The buffer list.
 * @param buffer_idx Index of buffer to get from the buffer list.
 * @param buffer Set to a newly allocated buffer object pointing to the
 *    underlying allocation in the buffer list corresponding to the buffer.
 * @return `TILEDB_OK` for success or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_buffer_list_get_buffer(
    tiledb_ctx_t* ctx,
    const tiledb_buffer_list_t* buffer_list,
    uint64_t buffer_idx,
    tiledb_buffer_t** buffer);

/**
 * Gets the total number of bytes in the buffers in the buffer list.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_buffer_list_t* buffer_list;
 * tiledb_buffer_list_alloc(ctx, &buffer_list);
 * uint64_t total_size;
 * tiledb_buffer_list_get_total_size(ctx, buffer_list, &total_size);
 * // total_size == 0 because the list is empty.
 * @endcode
 *
 * @param ctx TileDB context.
 * @param buffer_list The buffer list.
 * @param total_size Set to the total number of bytes in the buffers in the
 *    buffer list.
 * @return `TILEDB_OK` for success or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_buffer_list_get_total_size(
    tiledb_ctx_t* ctx,
    const tiledb_buffer_list_t* buffer_list,
    uint64_t* total_size);

/**
 * Copies and concatenates all the data in the buffer list into a new buffer.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_buffer_t* buff;
 * tiledb_buffer_list_flatten(ctx, buffer_list, &buff);
 * // ...
 * tiledb_buffer_free(&buff);
 * @endcode
 *
 * @param ctx TileDB context.
 * @param buffer_list The buffer list.
 * @param buffer Will be set to a newly allocated buffer holding a copy of the
 *    concatenated data from the buffer list.
 * @return `TILEDB_OK` for success or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_buffer_list_flatten(
    tiledb_ctx_t* ctx,
    const tiledb_buffer_list_t* buffer_list,
    tiledb_buffer_t** buffer);

/* ********************************* */
/*              CONFIG               */
/* ********************************* */

/**
 * Creates a TileDB config.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_config_t* config;
 * tiledb_error_t* error = NULL;
 * tiledb_config_alloc(&config, &error);
 * @endcode
 *
 * @param config The config to be created.
 * @param error Error object returned upon error (`NULL` if there is
 *     no error).
 * @return `TILEDB_OK` for success and `TILEDB_OOM` or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_config_alloc(tiledb_config_t** config, tiledb_error_t** error);

/**
 * Frees a TileDB config.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_config_t* config;
 * tiledb_error_t* error = NULL;
 * tiledb_config_alloc(&config, &error);
 * tiledb_config_free(&config);
 * @endcode
 *
 * @param config The config to be freed.
 */
TILEDB_EXPORT void tiledb_config_free(tiledb_config_t** config);

/**
 * Sets a config parameter.
 *
 * **Parameters**
 *
 * - `sm.dedup_coords` <br>
 *    If `true`, cells with duplicate coordinates will be removed during sparse
 *    fragment writes. Note that ties during deduplication are broken
 *    arbitrarily. <br>
 *    **Default**: false
 * - `sm.check_coord_dups` <br>
 *    This is applicable only if `sm.dedup_coords` is `false`.
 *    If `true`, an error will be thrown if there are cells with duplicate
 *    coordinates during sparse fragmnet writes. If `false` and there are
 *    duplicates, the duplicates will be written without errors. <br>
 *    **Default**: true
 * - `sm.check_coord_oob` <br>
 *    If `true`, an error will be thrown if there are cells with coordinates
 *    lying outside the domain during sparse fragment writes.  <br>
 *    **Default**: true
 *    `sm.read_range_oob` <br>
 *    If `error`, this will check ranges for read with out-of-bounds on the
 *    dimension domain's. If `warn`, the ranges will be capped at the
 *    dimension's domain and a warning logged. <br>
 *    **Default**: warn
 * - `sm.check_global_order` <br>
 *    Checks if the coordinates obey the global array order. Applicable only
 *    to sparse writes in global order.
 *    **Default**: true
 * - `sm.tile_cache_size` <br>
 *    The tile cache size in bytes. Any `uint64_t` value is acceptable. <br>
 *    **Default**: 10,000,000
 * - `sm.enable_signal_handlers` <br>
 *    Determines whether or not TileDB will install signal handlers. <br>
 *    **Default**: true
 * - `sm.compute_concurrency_level` <br>
 *    Upper-bound on number of threads to allocate for compute-bound tasks. <br>
 *    **Default*: # cores
 * - `sm.io_concurrency_level` <br>
 *    Upper-bound on number of threads to allocate for IO-bound tasks. <br>
 *    **Default*: # cores
 * - `sm.vacuum.mode` <br>
 *    The vacuuming mode, one of `fragments` (remove consolidated fragments),
 *    `fragment_meta` (remove only consolidated fragment metadata), or
 *    `array_meta` (remove consolidated array metadata files). <br>
 *    **Default**: fragments
 * - `sm.vacuum.timestamp_start` <br>
 *    **Experimental** <br>
 *    When set, an array will be vacuumed between this value and
 *    `sm.vacuum.timestamp_end` (inclusive). <br>
 *    Only for `fragments` and `array_meta` vacuum mode. <br>
 *    **Default**: 0
 * - `sm.vacuum.timestamp_end` <br>
 *    **Experimental** <br>
 *    When set, an array will be vacuumed between `sm.vacuum.timestamp_start`
 *    and this value (inclusive). <br>
 *    Only for `fragments` and `array_meta` vacuum mode. <br>
 *    **Default**: UINT64_MAX
 * - `sm.consolidation_mode` <br>
 *    The consolidation mode, one of `fragments` (consolidate all fragments),
 *    `fragment_meta` (consolidate only fragment metadata footers to a single
 *    file), or `array_meta` (consolidate array metadata only). <br>
 *    **Default**: "fragments"
 * - `sm.consolidation.amplification` <br>
 *    The factor by which the size of the dense fragment resulting
 *    from consolidating a set of fragments (containing at least one
 *    dense fragment) can be amplified. This is important when
 *    the union of the non-empty domains of the fragments to be
 *    consolidated have a lot of empty cells, which the consolidated
 *    fragment will have to fill with the special fill value
 *    (since the resulting fragment is dense). <br>
 *    **Default**: 1.0
 * - `sm.consolidation.buffer_size` <br>
 *    The size (in bytes) of the attribute buffers used during
 *    consolidation. <br>
 *    **Default**: 50,000,000
 * - `sm.consolidation.steps` <br>
 *    The number of consolidation steps to be performed when executing
 *    the consolidation algorithm.<br>
 *    **Default**: 1
 * - `sm.consolidation.step_min_frags` <br>
 *    The minimum number of fragments to consolidate in a single step.<br>
 *    **Default**: UINT32_MAX
 * - `sm.consolidation.step_max_frags` <br>
 *    The maximum number of fragments to consolidate in a single step.<br>
 *    **Default**: UINT32_MAX
 * - `sm.consolidation.step_size_ratio` <br>
 *    The size ratio that two ("adjacent") fragments must satisfy to be
 *    considered for consolidation in a single step.<br>
 *    **Default**: 0.0
 * - `sm.consolidation.timestamp_start` <br>
 *    **Experimental** <br>
 *    When set, an array will be consolidated between this value and
 *    `sm.consolidation.timestamp_end` (inclusive). <br>
 *    Only for `fragments` and `array_meta` consolidation mode. <br>
 *    **Default**: 0
 * - `sm.consolidation.timestamp_end` <br>
 *    **Experimental** <br>
 *    When set, an array will be consolidated between
 *    `sm.consolidation.timestamp_start` and this value (inclusive). <br>
 *    Only for `fragments` and `array_meta` consolidation mode. <br>
 *    **Default**: UINT64_MAX
 * - `sm.memory_budget` <br>
 *    The memory budget for tiles of fixed-sized attributes (or offsets for
 *    var-sized attributes) to be fetched during reads.<br>
 *    **Default**: 5GB
 * - `sm.memory_budget_var` <br>
 *    The memory budget for tiles of var-sized attributes
 *    to be fetched during reads.<br>
 *    **Default**: 10GB
 * - `sm.var_offsets.bitsize` <br>
 *    The size of offsets in bits to be used for offset buffers of var-sized
 *    attributes<br>
 *    **Default**: 64
 * - `sm.var_offsets.extra_element` <br>
 *    Add an extra element to the end of the offsets buffer of var-sized
 *    attributes which will point to the end of the values buffer.<br>
 *    **Default**: false
 * - `sm.var_offsets.mode` <br>
 *    The offsets format (`bytes` or `elements`) to be used for
 *    var-sized attributes.<br>
 *    **Default**: bytes
 * - `sm.sub_partitioner_memory_budget` <br>
 *    The memory budget used by the read algorithm to force partition the
 *    query range in case sorting is much slower than the partitioning
 *    overhead. <br>
 *    **Default**: 0
 * - `vfs.read_ahead_size` <br>
 *    The maximum byte size to read-ahead from the backend. <br>
 *    **Default**: 102400
 * -  `vfs.read_ahead_cache_size` <br>
 *    The the total maximum size of the read-ahead cache, which is an LRU. <br>
 *    **Default**: 10485760
 * - `vfs.min_parallel_size` <br>
 *    The minimum number of bytes in a parallel VFS operation
 *    (except parallel S3 writes, which are controlled by
 *    `vfs.s3.multipart_part_size`). <br>
 *    **Default**: 10MB
 * - `vfs.min_batch_size` <br>
 *    The minimum number of bytes in a VFS read operation<br>
 *    **Default**: 20MB
 * - `vfs.min_batch_gap` <br>
 *    The minimum number of bytes between two VFS read batches.<br>
 *    **Default**: 500KB
 * - `vfs.file.posix_file_permissions` <br>
 *    Permissions to use for posix file system with file creation.<br>
 *    **Default**: 644
 * - `vfs.file.posix_directory_permissions` <br>
 *    Permissions to use for posix file system with directory creation.<br>
 *    **Default**: 755
 * - `vfs.file.max_parallel_ops` <br>
 *    The maximum number of parallel operations on objects with `file:///`
 *    URIs. <br>
 *    **Default**: `sm.io_concurrency_level`
 * - `vfs.file.enable_filelocks` <br>
 *    If set to `false`, file locking operations are no-ops for `file:///` URIs
 *    in VFS. <br>
 *    **Default**: `true`
 * - `vfs.azure.storage_account_name` <br>
 *    Set the Azure Storage Account name. <br>
 *    **Default**: ""
 * - `vfs.azure.storage_account_key` <br>
 *    Set the Azure Storage Account key. <br>
 *    **Default**: ""
 * - `vfs.azure.blob_endpoint` <br>
 *    Overrides the default Azure Storage Blob endpoint. If empty, the endpoint
 *    will be constructed from the storage account name. This should not include
 *    an http:// or https:// prefix. <br>
 *    **Default**: ""
 * - `vfs.azure.block_list_block_size` <br>
 *    The block size (in bytes) used in Azure blob block list writes.
 *    Any `uint64_t` value is acceptable. Note: `vfs.azure.block_list_block_size
 *    vfs.azure.max_parallel_ops` bytes will be buffered before issuing block
 *    uploads in parallel. <br>
 *    **Default**: "5242880"
 * - `vfs.azure.max_parallel_ops` <br>
 *    The maximum number of Azure backend parallel operations. <br>
 *    **Default**: `sm.io_concurrency_level`
 * - `vfs.azure.use_block_list_upload` <br>
 *    Determines if the Azure backend can use chunked block uploads. <br>
 *    **Default**: "true"
 * - `vfs.azure.use_https` <br>
 *    Determines if the blob endpoint should use HTTP or HTTPS.
 *    **Default**: "true"
 * - `vfs.gcs.project_id` <br>
 *    Set the GCS project id. <br>
 *    **Default**: ""
 * - `vfs.gcs.multi_part_size` <br>
 *    The part size (in bytes) used in GCS multi part writes.
 *    Any `uint64_t` value is acceptable. Note:
 *    `vfs.gcs.multi_part_size * vfs.gcs.max_parallel_ops` bytes will
 *    be buffered before issuing part uploads in parallel. <br>
 *    **Default**: "5242880"
 * - `vfs.gcs.max_parallel_ops` <br>
 *    The maximum number of GCS backend parallel operations. <br>
 *    **Default**: `sm.io_concurrency_level`
 * - `vfs.gcs.use_multi_part_upload` <br>
 *    Determines if the GCS backend can use chunked part uploads. <br>
 *    **Default**: "true"
 * - `vfs.gcs.request_timeout_ms` <br>
 *    The maximum amount of time to retry network requests to GCS. <br>
 *    **Default**: "3000"
 * - `vfs.s3.region` <br>
 *    The S3 region, if S3 is enabled. <br>
 *    **Default**: us-east-1
 * - `vfs.s3.aws_access_key_id` <br>
 *    Set the AWS_ACCESS_KEY_ID <br>
 *    **Default**: ""
 * - `vfs.s3.aws_secret_access_key` <br>
 *    Set the AWS_SECRET_ACCESS_KEY <br>
 *    **Default**: ""
 * - `vfs.s3.aws_session_token` <br>
 *    Set the AWS_SESSION_TOKEN <br>
 *    **Default**: ""
 * - `vfs.s3.aws_role_arn` <br>
 *    Determines the role that we want to assume.
 *    Set the AWS_ROLE_ARN <br>
 *    **Default**: ""
 * - `vfs.s3.aws_external_id` <br>
 *    Third party access ID to your resources when assuming a role.
 *    Set the AWS_EXTERNAL_ID <br>
 *    **Default**: ""
 * - `vfs.s3.aws_load_frequency` <br>
 *    Session time limit when assuming a role.
 *    Set the AWS_LOAD_FREQUENCY <br>
 *    **Default**: ""
 * - `vfs.s3.aws_session_name` <br>
 *    (Optional) session name when assuming a role.
 *    Can be used for tracing and bookkeeping.
 *    Set the AWS_SESSION_NAME <br>
 *    **Default**: ""
 * - `vfs.s3.scheme` <br>
 *    The S3 scheme (`http` or `https`), if S3 is enabled. <br>
 *    **Default**: https
 * - `vfs.s3.endpoint_override` <br>
 *    The S3 endpoint, if S3 is enabled. <br>
 *    **Default**: ""
 * - `vfs.s3.use_virtual_addressing` <br>
 *    The S3 use of virtual addressing (`true` or `false`), if S3 is
 *    enabled. <br>
 *    **Default**: true
 * - `vfs.s3.skip_init` <br>
 *    Skip Aws::InitAPI for the S3 layer (`true` or `false`) <br>
 *    **Default**: false
 * - `vfs.s3.use_multipart_upload` <br>
 *    The S3 use of multi-part upload requests (`true` or `false`), if S3 is
 *    enabled. <br>
 *    **Default**: true
 * - `vfs.s3.max_parallel_ops` <br>
 *    The maximum number of S3 backend parallel operations. <br>
 *    **Default**: `sm.io_concurrency_level`
 * - `vfs.s3.multipart_part_size` <br>
 *    The part size (in bytes) used in S3 multipart writes.
 *    Any `uint64_t` value is acceptable. Note: `vfs.s3.multipart_part_size *
 *    vfs.s3.max_parallel_ops` bytes will be buffered before issuing multipart
 *    uploads in parallel. <br>
 *    **Default**: 5MB
 * - `vfs.s3.ca_file` <br>
 *    Path to SSL/TLS certificate file to be used by cURL for for S3 HTTPS
 *    encryption. Follows cURL conventions:
 *    https://curl.haxx.se/docs/manpage.html
 *    **Default**: ""
 * - `vfs.s3.ca_path` <br>
 *    Path to SSL/TLS certificate directory to be used by cURL for S3 HTTPS
 *    encryption. Follows cURL conventions:
 *    https://curl.haxx.se/docs/manpage.html
 *    **Default**: ""
 * - `vfs.s3.connect_timeout_ms` <br>
 *    The connection timeout in ms. Any `long` value is acceptable. <br>
 *    **Default**: 3000
 * - `vfs.s3.connect_max_tries` <br>
 *    The maximum tries for a connection. Any `long` value is acceptable. <br>
 *    **Default**: 5
 * - `vfs.s3.connect_scale_factor` <br>
 *    The scale factor for exponential backoff when connecting to S3.
 *    Any `long` value is acceptable. <br>
 *    **Default**: 25
 * - `vfs.s3.logging_level` <br>
 *    The AWS SDK logging level. This is a process-global setting. The
 *    configuration of the most recently constructed context will set
 *    process state. Log files are written to the process working directory.
 *    **Default**: ""
 * - `vfs.s3.request_timeout_ms` <br>
 *    The request timeout in ms. Any `long` value is acceptable. <br>
 *    **Default**: 3000
 * - `vfs.s3.requester_pays` <br>
 *    The requester pays for the S3 access charges. <br>
 *    **Default**: false
 * - `vfs.s3.proxy_host` <br>
 *    The S3 proxy host. <br>
 *    **Default**: ""
 * - `vfs.s3.proxy_port` <br>
 *    The S3 proxy port. <br>
 *    **Default**: 0
 * - `vfs.s3.proxy_scheme` <br>
 *    The S3 proxy scheme. <br>
 *    **Default**: "http"
 * - `vfs.s3.proxy_username` <br>
 *    The S3 proxy username. Note: this parameter is not serialized by
 *    `tiledb_config_save_to_file`. <br>
 *    **Default**: ""
 * - `vfs.s3.proxy_password` <br>
 *    The S3 proxy password. Note: this parameter is not serialized by
 *    `tiledb_config_save_to_file`. <br>
 *    **Default**: ""
 * - `vfs.s3.verify_ssl` <br>
 *    Enable HTTPS certificate verification. <br>
 *    **Default**: true""
 * - `vfs.s3.sse` <br>
 *    The server-side encryption algorithm to use. Supported non-empty
 *    values are "aes256" and "kms" (AWS key management service). <br>
 *    **Default**: ""
 * - `vfs.hdfs.name_node_uri"` <br>
 *    Name node for HDFS. <br>
 *    **Default**: ""
 * - `vfs.hdfs.username` <br>
 *    HDFS username. <br>
 *    **Default**: ""
 * - `vfs.hdfs.kerb_ticket_cache_path` <br>
 *    HDFS kerb ticket cache path. <br>
 *    **Default**: ""
 * - `config.env_var_prefix` <br>
 *    Prefix of environmental variables for reading configuration
 *    parameters. <br>
 *    **Default**: "TILEDB_"
 * - `config.logging_level` <br>
 *    The logging level configured, possible values: "0": fatal, "1": error,
 *    "2": warn, "3": info "4": debug, "5": trace <br>
 *    **Default**: "1" if --enable-verbose bootstrap flag is provided,
 *    "0" otherwise <br>
 * - `rest.server_address` <br>
 *    URL for REST server to use for remote arrays. <br>
 *    **Default**: "https://api.tiledb.com"
 * - `rest.server_serialization_format` <br>
 *    Serialization format to use for remote array requests (CAPNP or
 *    JSON). <br>
 *    **Default**: "CAPNP"
 * - `rest.username` <br>
 *    Username for login to REST server. <br>
 *    **Default**: ""
 * - `rest.password` <br>
 *    Password for login to REST server. <br>
 *    **Default**: ""
 * - `rest.token` <br>
 *    Authentication token for REST server (used instead of
 *    username/password). <br>
 *    **Default**: ""
 * - `rest.resubmit_incomplete` <br>
 *    If true, incomplete queries received from server are automatically
 *    resubmitted before returning to user control. <br>
 *    **Default**: "true"
 * - `rest.ignore_ssl_validation` <br>
 *    Have curl ignore ssl peer and host validation for REST server. <br>
 *    **Default**: false
 * - `rest.creation_access_credentials_name` <br>
 *    The name of the registered access key to use for creation of the REST
 *    server. <br>
 *    **Default**: no default set
 * - `rest.retry_http_codes` <br>
 *    CSV list of http status codes to automatically retry a REST request for
 *    <br>
 *    **Default**: "503"
 * - `rest.retry_count` <br>
 *    Number of times to retry failed REST requests <br>
 *    **Default**: 3
 * - `rest.retry_initial_delay_ms` <br>
 *    Initial delay in milliseconds to wait until retrying a REST request <br>
 *    **Default**: 500
 * - `rest.retry_delay_factor` <br>
 *    The delay factor to exponentially wait until further retries of a failed
 *    REST request <br>
 *    **Default**: 1.25
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_error_t* error = NULL;
 * tiledb_config_set(config, "sm.tile_cache_size", "1000000", &error);
 * @endcode
 *
 * @param config The config object.
 * @param param The parameter to be set.
 * @param value The value of the parameter to be set.
 * @param error Error object returned upon error (`NULL` if there is
 *     no error).
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_config_set(
    tiledb_config_t* config,
    const char* param,
    const char* value,
    tiledb_error_t** error);

/**
 * Gets a config parameter.
 *
 * **Example:**
 *
 * @code{.c}
 * const char* value;
 * tiledb_error_t* error = NULL;
 * tiledb_config_get(config, "sm.tile_cache_size", &value, &error);
 * @endcode
 *
 * @param config The config object.
 * @param param The parameter to be set.
 * @param value A pointer to the value of the parameter to be retrieved
 *    (`NULL` if it does not exist).
 * @param error Error object returned upon error (`NULL` if there is
 *     no error).
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_config_get(
    tiledb_config_t* config,
    const char* param,
    const char** value,
    tiledb_error_t** error);

/**
 * Loads config parameters from a (local) text file.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_error_t* error = NULL;
 * tiledb_config_load_from_file(config, "tiledb.conf", &error);
 * @endcode
 *
 * @param config The config object.
 * @param filename The name of the file.
 * @param error Error object returned upon error (`NULL` if there is
 *     no error).
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_config_load_from_file(
    tiledb_config_t* config, const char* filename, tiledb_error_t** error);

/**
 * Unsets a config parameter. This will set the config parameter to its
 * default value.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_error_t* error = NULL;
 * tiledb_config_unset(config, "sm.tile_cache_size", &error);
 * @endcode
 *
 * @param config The config object.
 * @param param The parameter to be unset.
 * @param error Error object returned upon error (`NULL` if there is
 *     no error).
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_config_unset(
    tiledb_config_t* config, const char* param, tiledb_error_t** error);

/**
 * Saves the config parameters to a (local) text file.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_error_t* error = NULL;
 * tiledb_config_save_to_file(config, "tiledb.conf", &error);
 * @endcode
 *
 * @param config The config object.
 * @param filename The name of the file.
 * @param error Error object returned upon error (`NULL` if there is
 *     no error).
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_config_save_to_file(
    tiledb_config_t* config, const char* filename, tiledb_error_t** error);

/**
 * Compares 2 configurations for equality
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_error_t error;
 * uint8_t equal;
 * tiledb_config_compare(lhs, rhs, &equal);
 * @endcode
 *
 * @param lhs The left-hand side config object.
 * @param rhs The right-hand side config object.
 * @param equal Integer of equality comparison
 *      1 = true, 0 = false
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_config_compare(
    tiledb_config_t* lhs, tiledb_config_t* rhs, uint8_t* equal);

/* ****************************** */
/*            CONFIG ITER         */
/* ****************************** */

/**
 * Creates an iterator on a config object.
 *
 * **Examples:**
 *
 * The following creates a config iterator without a prefix. This
 * will iterate over all config param/values.
 *
 * @code{.c}
 * tiledb_error_t* error = NULL;
 * tiledb_config_iter_t* config_iter;
 * tiledb_config_iter_alloc(config, NULL, &config_iter, &error);
 * @endcode
 *
 * The following creates a config iterator with a prefix. This
 * will iterate over all S3 config param/values, stripping out
 * `vfs.s3.`. For instance, instead of retrieving `vfs.s3.region`
 * as a parameter via `tiledb_config_iter_here`, it will retrieve
 * `region`.
 *
 * @code{.c}
 * tiledb_error_t* error = NULL;
 * tiledb_config_iter_t* config_iter;
 * tiledb_config_iter_alloc(config, "vfs.s3.", &config_iter, &error);
 * @endcode
 *
 * @param config A config object the iterator will operate on.
 * @param prefix If not `NULL`, only the config parameters starting
 *     with `prefix*` will be iterated on. Moreover, the prefix will
 *     be stripped from the parameters. Otherwise, all parameters will
 *     be iterated on and their full name will be retrieved.
 * @param config_iter The config iterator to be created.
 * @param error Error object returned upon error (`NULL` if there is
 *     no error).
 * @return `TILEDB_OK` for success and `TILEDB_OOM` or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_config_iter_alloc(
    tiledb_config_t* config,
    const char* prefix,
    tiledb_config_iter_t** config_iter,
    tiledb_error_t** error);

/**
 * Resets the iterator.
 *
 * **Examples:**
 *
 * Without a prefix:
 *
 * @code{.c}
 * tiledb_config_iter_reset(config, config_iter, NULL, &error);
 * @endcode
 *
 * With a prefix:
 *
 * @code{.c}
 * tiledb_config_iter_reset(config, config_iter, "vfs.s3.", &error);
 * @endcode
 *
 * @param config A config object the iterator will operate on.
 * @param config_iter The config iterator to be reset.
 * @param prefix If not `NULL`, only the config parameters starting
 *     with `prefix*` will be iterated on. Moreover, the prefix will
 *     be stripped from the parameters. Otherwise, all parameters will
 *     be iterated on and their full name will be retrieved.
 * @param error Error object returned upon error (`NULL` if there is
 *     no error).
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_config_iter_reset(
    tiledb_config_t* config,
    tiledb_config_iter_t* config_iter,
    const char* prefix,
    tiledb_error_t** error);

/**
 * Frees a config iterator.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_config_iter_free(&config_iter);
 * @endcode
 *
 * @param config_iter The config iterator to be freed.
 */
TILEDB_EXPORT void tiledb_config_iter_free(tiledb_config_iter_t** config_iter);

/**
 * Retrieves the config param and value currently pointed by the iterator.
 *
 * **Example:**
 *
 * @code{.c}
 * const char *param, *value;
 * tiledb_config_iter_here(config_iter, &param, &value, &error);
 * @endcode
 *
 * @param config_iter The config iterator.
 * @param param The config parameter to be retrieved (`NULL` if the iterator
 *     is at the end).
 * @param value The config value to be retrieved (`NULL` if the iterator
 *     is at the end).
 * @param error Error object returned upon error (`NULL` if there is
 *     no error).
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_config_iter_here(
    tiledb_config_iter_t* config_iter,
    const char** param,
    const char** value,
    tiledb_error_t** error);

/**
 * Moves the iterator to the next param.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_config_iter_next(config_iter, &error);
 * @endcode
 *
 * @param config_iter The config iterator.
 * @param error Error object returned upon error (`NULL` if there is
 *     no error).
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_config_iter_next(
    tiledb_config_iter_t* config_iter, tiledb_error_t** error);

/**
 * Checks if the iterator is done.
 *
 * **Example:**
 *
 * @code{.c}
 * int32_t done;
 * tiledb_config_iter_done(config_iter, &done, &error);
 * @endcode
 *
 * @param config_iter The config iterator.
 * @param done Sets this to `1` if the iterator is done, `0` otherwise.
 * @param error Error object returned upon error (`NULL` if there is
 *     no error).
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_config_iter_done(
    tiledb_config_iter_t* config_iter, int32_t* done, tiledb_error_t** error);

/* ********************************* */
/*              CONTEXT              */
/* ********************************* */

/**
 * Creates a TileDB context, which contains the TileDB storage manager
 * that manages everything in the TileDB library.
 *
 * **Examples:**
 *
 * Without config (i.e., use default configuration):
 *
 * @code{.c}
 * tiledb_ctx_t* ctx;
 * tiledb_ctx_alloc(NULL, &ctx);
 * @endcode
 *
 * With some config:
 *
 * @code{.c}
 * tiledb_ctx_t* ctx;
 * tiledb_ctx_alloc(config, &ctx);
 * @endcode
 *
 * @param config The configuration parameters (`NULL` means default).
 * @param ctx The TileDB context to be created.
 * @return `TILEDB_OK` for success and `TILEDB_OOM` or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_ctx_alloc(tiledb_config_t* config, tiledb_ctx_t** ctx);

/**
 * Destroys the TileDB context, freeing all associated memory and resources.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_ctx_t* ctx;
 * tiledb_ctx_alloc(NULL, &ctx);
 * tiledb_ctx_free(&ctx);
 * @endcode
 *
 * @param ctx The TileDB context to be freed.
 */
TILEDB_EXPORT void tiledb_ctx_free(tiledb_ctx_t** ctx);

/**
 * Retrieves the stats from a TileDB context.
 *
 * **Example:**
 *
 * @code{.c}
 * char* stats_json;
 * tiledb_ctx_get_stats(ctx, &stats_json);
 * // Make sure to free the retrieved `stats_json`
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param stats_json The output json. The caller takes ownership
 *   of the c-string.
 * @return `TILEDB_OK` for success and `TILEDB_OOM` or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_ctx_get_stats(tiledb_ctx_t* ctx, char** stats_json);

/**
 * Retrieves a copy of the config from a TileDB context.
 * Modifying this config will not affect the initialized
 * context configuration.
 *
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_config_t* config;
 * tiledb_ctx_get_config(ctx, &config);
 * // Make sure to free the retrieved config
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param config The config to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_OOM` or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_ctx_get_config(tiledb_ctx_t* ctx, tiledb_config_t** config);

/**
 * Retrieves the last TileDB error associated with a TileDB context.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_error_t* error;
 * tiledb_ctx_get_last_error(ctx, &error);
 * // Make sure to free the retrieved error, checking first if it is NULL
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param err The last error, or `NULL` if no error has been raised.
 * @return `TILEDB_OK` for success and `TILEDB_OOM` or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_ctx_get_last_error(tiledb_ctx_t* ctx, tiledb_error_t** err);

/**
 * Checks if a given storage filesystem backend is supported.
 *
 * **Example:**
 *
 * @code{.c}
 * int32_t is_supported;
 * tiledb_ctx_is_supported_fs(ctx, TILEDB_HDFS, &is_supported);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param fs The filesystem to be checked.
 * @param is_supported Sets it to `1` if the filesystem is supported, and
 * `0` otherwise.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_ctx_is_supported_fs(
    tiledb_ctx_t* ctx, tiledb_filesystem_t fs, int32_t* is_supported);

/**
 * Cancels all background or async tasks associated with the given context.
 *
 * @param ctx The TileDB context.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_ctx_cancel_tasks(tiledb_ctx_t* ctx);

/**
 * Sets a string key-value "tag" on the given context.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_ctx_set_tag(ctx, "tag key", "tag value");
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param key The tag key
 * @param value The tag value.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_ctx_set_tag(tiledb_ctx_t* ctx, const char* key, const char* value);

/* ********************************* */
/*                GROUP              */
/* ********************************* */

/**
 * Creates a new TileDB group.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_group_create(ctx, "my_group");
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param group_uri The group URI.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_group_create(tiledb_ctx_t* ctx, const char* group_uri);

/* ********************************* */
/*              FILTER               */
/* ********************************* */

/**
 * Creates a TileDB filter.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_filter_t* filter;
 * tiledb_filter_alloc(ctx, TILEDB_FILTER_BZIP2, &filter);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param type The filter type.
 * @param filter The TileDB filter to be created.
 * @return `TILEDB_OK` for success and `TILEDB_OOM` or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_filter_alloc(
    tiledb_ctx_t* ctx, tiledb_filter_type_t type, tiledb_filter_t** filter);

/**
 * Destroys a TileDB filter, freeing associated memory.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_filter_t* filter;
 * tiledb_filter_alloc(ctx, TILEDB_FILTER_BZIP2, &filter);
 * tiledb_filter_free(&filter);
 * @endcode
 *
 * @param filter The filter to be destroyed.
 */
TILEDB_EXPORT void tiledb_filter_free(tiledb_filter_t** filter);

/**
 * Retrieves the type of a filter.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_filter_t* filter;
 * tiledb_filter_alloc(ctx, TILEDB_FILTER_BZIP2, &filter);
 * tiledb_filter_type_t type;
 * tiledb_filter_get_type(ctx, filter, &type);
 * // type == TILEDB_FILTER_BZIP2
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param filter The TileDB filter.
 * @param type The filter type to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_OOM` or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_filter_get_type(
    tiledb_ctx_t* ctx, tiledb_filter_t* filter, tiledb_filter_type_t* type);

/**
 * Sets an option on a filter. Options are filter dependent; this function
 * returns an error if the given option is not valid for the given filter.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_filter_t* filter;
 * tiledb_filter_alloc(ctx, TILEDB_FILTER_BZIP2, &filter);
 * int32_t level = 5;
 * tiledb_filter_set_option(ctx, filter, TILEDB_COMPRESSION_LEVEL, &level);
 * tiledb_filter_free(&filter);
 * @endcode
 *
 * @param ctx TileDB context.
 * @param filter The target filter.
 * @param option Filter option to set.
 * @param value Value of option to set.
 * @return `TILEDB_OK` for success or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_filter_set_option(
    tiledb_ctx_t* ctx,
    tiledb_filter_t* filter,
    tiledb_filter_option_t option,
    const void* value);

/**
 * Gets an option value from a filter. Options are filter dependent; this
 * function returns an error if the given option is not valid for the given
 * filter.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_filter_t* filter;
 * tiledb_filter_alloc(ctx, TILEDB_FILTER_BZIP2, &filter);
 * int32_t level;
 * tiledb_filter_get_option(ctx, filter, TILEDB_COMPRESSION_LEVEL, &level);
 * // level == -1 (the default)
 * tiledb_filter_free(&filter);
 * @endcode
 *
 * @note The buffer pointed to by `value` must be large enough to hold the
 * option value.
 *
 * @param ctx TileDB context.
 * @param filter The target filter.
 * @param option Filter option to get.
 * @param value Buffer that option value will be written to.
 * @return `TILEDB_OK` for success or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_filter_get_option(
    tiledb_ctx_t* ctx,
    tiledb_filter_t* filter,
    tiledb_filter_option_t option,
    void* value);

/* ********************************* */
/*            FILTER LIST            */
/* ********************************* */

/**
 * Creates a TileDB filter list (pipeline of filters).
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_filter_list_t* filter_list;
 * tiledb_filter_list_alloc(ctx, &filter_list);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param filter_list The TileDB filter list to be created.
 * @return `TILEDB_OK` for success and `TILEDB_OOM` or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_filter_list_alloc(tiledb_ctx_t* ctx, tiledb_filter_list_t** filter_list);

/**
 * Destroys a TileDB filter list, freeing associated memory.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_filter_list_t* filter_list;
 * tiledb_filter_list_alloc(ctx, &filter_list);
 * tiledb_filter_list_free(&filter_list);
 * @endcode
 *
 * @param filter_list The filter list to be destroyed.
 */
TILEDB_EXPORT void tiledb_filter_list_free(tiledb_filter_list_t** filter_list);

/**
 * Appends a filter to a filter list. Data is processed through each filter in
 * the order the filters were added.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_filter_t* filter;
 * tiledb_filter_alloc(ctx, TILEDB_FILTER_BZIP2, &filter);
 *
 * tiledb_filter_list_t* filter_list;
 * tiledb_filter_list_alloc(ctx, &filter_list);
 * tiledb_filter_list_add_filter(ctx, filter_list, filter);
 *
 * tiledb_filter_list_free(&filter_list);
 * tiledb_filter_free(&filter);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param filter_list The target filter list.
 * @param filter The filter to add.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_filter_list_add_filter(
    tiledb_ctx_t* ctx,
    tiledb_filter_list_t* filter_list,
    tiledb_filter_t* filter);

/**
 * Sets the maximum tile chunk size for a filter list.
 *
 * **Example:**
 *
 * @code{.c}
 * // Set max chunk size to 16KB:
 * tiledb_filter_list_set_max_chunk_size(ctx, filter_list, 16384);
 * @endcode
 *
 * @param ctx The TileDB context
 * @param filter_list The target filter list
 * @param max_chunk_size The max chunk size value to set
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_filter_list_set_max_chunk_size(
    tiledb_ctx_t* ctx,
    const tiledb_filter_list_t* filter_list,
    uint32_t max_chunk_size);

/**
 * Retrieves the number of filters in a filter list.
 *
 * **Example:**
 *
 * @code{.c}
 * uint32_t num_filters;
 * tiledb_filter_list_get_nfilters(ctx, filter_list, &num_filters);
 * @endcode
 *
 * @param ctx The TileDB context
 * @param filter_list The filter list
 * @param nfilters The number of filters on the filter list
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_filter_list_get_nfilters(
    tiledb_ctx_t* ctx,
    const tiledb_filter_list_t* filter_list,
    uint32_t* nfilters);

/**
 * Retrieves a filter object from a filter list by index.
 *
 * **Example:**
 *
 * The following retrieves the first filter from a filter list.
 *
 * @code{.c}
 * tiledb_filter_t* filter;
 * tiledb_filter_list_get_filter_from_index(ctx, filter_list, 0, &filter);
 * tiledb_filter_free(&filter);
 * @endcode
 *
 * @param ctx The TileDB context
 * @param filter_list The filter list to retrieve the filter from
 * @param index The index of the filter
 * @param filter The retrieved filter object.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_filter_list_get_filter_from_index(
    tiledb_ctx_t* ctx,
    const tiledb_filter_list_t* filter_list,
    uint32_t index,
    tiledb_filter_t** filter);

/**
 * Gets the maximum tile chunk size for a filter list.
 *
 * **Example:**
 *
 * @code{.c}
 * uint32_t max_chunk_size;
 * tiledb_filter_list_get_max_chunk_size(ctx, filter_list, &max_chunk_size);
 * @endcode
 *
 * @param ctx The TileDB context
 * @param filter_list The target filter list
 * @param max_chunk_size The retrieved max chunk size value
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_filter_list_get_max_chunk_size(
    tiledb_ctx_t* ctx,
    const tiledb_filter_list_t* filter_list,
    uint32_t* max_chunk_size);

/* ********************************* */
/*            ATTRIBUTE              */
/* ********************************* */

/**
 * Creates a TileDB attribute.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_attribute_t* attr;
 * tiledb_attribute_alloc(ctx, "my_attr", TILEDB_INT32, &attr);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param name The attribute name. Providing an empty string for the name
 * creates an anonymous attribute.
 * @param type The attribute type.
 * @param attr The TileDB attribute to be created.
 * @return `TILEDB_OK` for success and `TILEDB_OOM` or `TILEDB_ERR` for error.
 *
 * @note The default number of values per cell is 1.
 */
TILEDB_EXPORT int32_t tiledb_attribute_alloc(
    tiledb_ctx_t* ctx,
    const char* name,
    tiledb_datatype_t type,
    tiledb_attribute_t** attr);

/**
 * Destroys a TileDB attribute, freeing associated memory.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_attribute_t* attr;
 * tiledb_attribute_alloc(ctx, "my_attr", TILEDB_INT32, &attr);
 * tiledb_attribute_free(&attr);
 * @endcode
 *
 * @param attr The attribute to be destroyed.
 */
TILEDB_EXPORT void tiledb_attribute_free(tiledb_attribute_t** attr);

/**
 * Sets the nullability of an attribute.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_attribute_set_nullable(ctx, attr, 1);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param attr The target attribute.
 * @param nullable Non-zero if the attribute is nullable.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_attribute_set_nullable(
    tiledb_ctx_t* ctx, tiledb_attribute_t* attr, uint8_t nullable);

/**
 * Sets the filter list for an attribute.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_filter_list_t* filter_list;
 * tiledb_filter_list_alloc(ctx, &filter_list);
 * tiledb_filter_list_add_filter(ctx, filter_list, filter);
 * tiledb_attribute_set_filter_list(ctx, attr, filter_list);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param attr The target attribute.
 * @param filter_list The filter_list to be set.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_attribute_set_filter_list(
    tiledb_ctx_t* ctx,
    tiledb_attribute_t* attr,
    tiledb_filter_list_t* filter_list);

/**
 * Sets the number of values per cell for an attribute. If this is not
 * used, the default is `1`.
 *
 * **Examples:**
 *
 * For a fixed-sized attribute:
 *
 * @code{.c}
 * tiledb_attribute_set_cell_val_num(ctx, attr, 3);
 * @endcode
 *
 * For a variable-sized attribute:
 *
 * @code{.c}
 * tiledb_attribute_set_cell_val_num(ctx, attr, TILEDB_VAR_NUM);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param attr The target attribute.
 * @param cell_val_num The number of values per cell.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_attribute_set_cell_val_num(
    tiledb_ctx_t* ctx, tiledb_attribute_t* attr, uint32_t cell_val_num);

/**
 * Retrieves the attribute name.
 *
 * **Example:**
 *
 * @code{.c}
 * const char* attr_name;
 * tiledb_attribute_get_name(ctx, attr, &attr_name);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param attr The attribute.
 * @param name The name to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_attribute_get_name(
    tiledb_ctx_t* ctx, const tiledb_attribute_t* attr, const char** name);

/**
 * Retrieves the attribute type.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_datatype_t attr_type;
 * tiledb_attribute_get_type(ctx, attr, &attr_type);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param attr The attribute.
 * @param type The type to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_attribute_get_type(
    tiledb_ctx_t* ctx, const tiledb_attribute_t* attr, tiledb_datatype_t* type);

/**
 * Sets the nullability of an attribute.
 *
 * **Example:**
 *
 * @code{.c}
 * uint8_t nullable;
 * tiledb_attribute_get_nullable(ctx, attr, &nullable);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param attr The target attribute.
 * @param nullable Output argument, non-zero for nullable and zero
 *    for non-nullable.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_attribute_get_nullable(
    tiledb_ctx_t* ctx, tiledb_attribute_t* attr, uint8_t* nullable);

/**
 * Retrieves the filter list for an attribute.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_filter_list_t* filter_list;
 * tiledb_attribute_get_filter_list(ctx, attr, &filter_list);
 * tiledb_filter_list_free(&filter_list);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param attr The target attribute.
 * @param filter_list The filter list to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_attribute_get_filter_list(
    tiledb_ctx_t* ctx,
    tiledb_attribute_t* attr,
    tiledb_filter_list_t** filter_list);

/**
 * Retrieves the number of values per cell for the attribute. For variable-sized
 * attributes result is TILEDB_VAR_NUM.
 *
 * **Example:**
 *
 * @code{.c}
 * uint32_t num;
 * tiledb_attribute_get_cell_val_num(ctx, attr, &num);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param attr The attribute.
 * @param cell_val_num The number of values per cell to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_attribute_get_cell_val_num(
    tiledb_ctx_t* ctx, const tiledb_attribute_t* attr, uint32_t* cell_val_num);

/**
 * Retrieves the cell size for this attribute.
 *
 * **Example:**
 *
 * @code{.c}
 * uint64_t cell_size;
 * tiledb_attribute_get_cell_size(ctx, attr, &cell_size);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param attr The attribute.
 * @param cell_size The cell size to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_attribute_get_cell_size(
    tiledb_ctx_t* ctx, const tiledb_attribute_t* attr, uint64_t* cell_size);

/**
 * Dumps the contents of an attribute in ASCII form to some output (e.g.,
 * file or stdout).
 *
 * **Example:**
 *
 * The following prints the attribute dump to standard output.
 *
 * @code{.c}
 * tiledb_attribute_dump(ctx, attr, stdout);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param attr The attribute.
 * @param out The output.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_attribute_dump(
    tiledb_ctx_t* ctx, const tiledb_attribute_t* attr, FILE* out);

/**
 * Sets the default fill value for the input attribute. This value will
 * be used for the input attribute whenever querying (1) an empty cell in
 * a dense array, or (2) a non-empty cell (in either dense or sparse array)
 * when values on the input attribute are missing (e.g., if the user writes
 * a subset of the attributes in a write operation).
 *
 * Applicable to var-sized attributes.
 *
 * **Example:**
 *
 * @code{.c}
 * // Assumming a int32 attribute
 * int32_t value = 0;
 * uint64_t size = sizeof(value);
 * tiledb_attribute_set_fill_value(ctx, attr, &value, size);
 *
 * // Assumming a var char attribute
 * const char* value = "foo";
 * uint64_t size = strlen(value);
 * tiledb_attribute_set_fill_value(ctx, attr, value, size);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param attr The target attribute.
 * @param value The fill value to set.
 * @param size The fill value size in bytes.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 *
 * @note A call to `tiledb_attribute_cell_val_num` sets the fill value
 *     of the attribute to its default. Therefore, make sure you invoke
 *     `tiledb_attribute_set_fill_value` after deciding on the number
 *     of values this attribute will hold in each cell.
 *
 * @note For fixed-sized attributes, the input `size` should be equal
 *     to the cell size.
 */
TILEDB_EXPORT int32_t tiledb_attribute_set_fill_value(
    tiledb_ctx_t* ctx,
    tiledb_attribute_t* attr,
    const void* value,
    uint64_t size);

/**
 * Gets the default fill value for the input attribute. This value will
 * be used for the input attribute whenever querying (1) an empty cell in
 * a dense array, or (2) a non-empty cell (in either dense or sparse array)
 * when values on the input attribute are missing (e.g., if the user writes
 * a subset of the attributes in a write operation).
 *
 * Applicable to both fixed-sized and var-sized attributes.
 *
 * **Example:**
 *
 * @code{.c}
 * // Assuming a int32 attribute
 * const int32_t* value;
 * uint64_t size;
 * tiledb_attribute_get_fill_value(ctx, attr, &value, &size);
 *
 * // Assuming a var char attribute
 * const char* value;
 * uint64_t size;
 * tiledb_attribute_get_fill_value(ctx, attr, &value, &size);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param attr The target attribute.
 * @param value A pointer to the fill value to get.
 * @param size The size of the fill value to get.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_attribute_get_fill_value(
    tiledb_ctx_t* ctx,
    tiledb_attribute_t* attr,
    const void** value,
    uint64_t* size);

/**
 * Sets the default fill value for the input, nullable attribute. This value
 * will be used for the input attribute whenever querying (1) an empty cell in
 * a dense array, or (2) a non-empty cell (in either dense or sparse array)
 * when values on the input attribute are missing (e.g., if the user writes
 * a subset of the attributes in a write operation).
 *
 * Applicable to var-sized attributes.
 *
 * **Example:**
 *
 * @code{.c}
 * // Assumming a int32 attribute
 * int32_t value = 0;
 * uint64_t size = sizeof(value);
 * uint8_t valid = 0;
 * tiledb_attribute_set_fill_value_nullable(ctx, attr, &value, size, valid);
 *
 * // Assumming a var char attribute
 * const char* value = "foo";
 * uint64_t size = strlen(value);
 * uint8_t valid = 1;
 * tiledb_attribute_set_fill_value_nullable(ctx, attr, value, size, valid);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param attr The target attribute.
 * @param value The fill value to set.
 * @param size The fill value size in bytes.
 * @param validity The validity fill value, zero for a null value and
 *     non-zero for a valid attribute.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 *
 * @note A call to `tiledb_attribute_cell_val_num` sets the fill value
 *     of the attribute to its default. Therefore, make sure you invoke
 *     `tiledb_attribute_set_fill_value_nullable` after deciding on the
 *     number of values this attribute will hold in each cell.
 *
 * @note For fixed-sized attributes, the input `size` should be equal
 *     to the cell size.
 */
TILEDB_EXPORT int32_t tiledb_attribute_set_fill_value_nullable(
    tiledb_ctx_t* ctx,
    tiledb_attribute_t* attr,
    const void* value,
    uint64_t size,
    uint8_t validity);

/**
 * Gets the default fill value for the input, nullable attribute. This value
 * will be used for the input attribute whenever querying (1) an empty cell in
 * a dense array, or (2) a non-empty cell (in either dense or sparse array)
 * when values on the input attribute are missing (e.g., if the user writes
 * a subset of the attributes in a write operation).
 *
 * Applicable to both fixed-sized and var-sized attributes.
 *
 * **Example:**
 *
 * @code{.c}
 * // Assuming a int32 attribute
 * const int32_t* value;
 * uint64_t size;
 * uint8_t valid;
 * tiledb_attribute_get_fill_value_nullable(ctx, attr, &value, &size, &valid);
 *
 * // Assuming a var char attribute
 * const char* value;
 * uint64_t size;
 * uint8_t valid;
 * tiledb_attribute_get_fill_value_nullable(ctx, attr, &value, &size, &valid);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param attr The target attribute.
 * @param value A pointer to the fill value to get.
 * @param size The size of the fill value to get.
 * @param valid The fill value validity to get.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_attribute_get_fill_value_nullable(
    tiledb_ctx_t* ctx,
    tiledb_attribute_t* attr,
    const void** value,
    uint64_t* size,
    uint8_t* valid);

/* ********************************* */
/*               DOMAIN              */
/* ********************************* */

/**
 * Creates a TileDB domain.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_domain_t* domain;
 * tiledb_domain_alloc(ctx, &domain);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param domain The TileDB domain to be created.
 * @return `TILEDB_OK` for success and `TILEDB_OOM` or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_domain_alloc(tiledb_ctx_t* ctx, tiledb_domain_t** domain);

/**
 * Destroys a TileDB domain, freeing associated memory.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_domain_t* domain;
 * tiledb_domain_alloc(ctx, &domain);
 * tiledb_domain_free(&domain);
 * @endcode
 *
 * @param domain The domain to be destroyed.
 */
TILEDB_EXPORT void tiledb_domain_free(tiledb_domain_t** domain);

/**
 * Retrieves the domain's type.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_datatype_t type;
 * tiledb_domain_get_type(ctx, domain, &type);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param domain The domain.
 * @param type The type to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_domain_get_type(
    tiledb_ctx_t* ctx, const tiledb_domain_t* domain, tiledb_datatype_t* type);

/**
 * Retrieves the number of dimensions in a domain.
 *
 * **Example:**
 *
 * @code{.c}
 * uint32_t dim_num;
 * tiledb_domain_get_ndim(ctx, domain, &dim_num);
 * @endcode
 *
 * @param ctx The TileDB context
 * @param domain The domain
 * @param ndim The number of dimensions in a domain.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_domain_get_ndim(
    tiledb_ctx_t* ctx, const tiledb_domain_t* domain, uint32_t* ndim);

/**
 * Adds a dimension to a TileDB domain.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_dimension_t* dim;
 * int64_t dim_domain[] = {1, 10};
 * int64_t tile_extent = 5;
 * tiledb_dimension_alloc(
 *     ctx, "dim_0", TILEDB_INT64, dim_domain, &tile_extent, &dim);
 * tiledb_domain_add_dimension(ctx, domain, dim);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param domain The domain to add the dimension to.
 * @param dim The dimension to be added.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_domain_add_dimension(
    tiledb_ctx_t* ctx, tiledb_domain_t* domain, tiledb_dimension_t* dim);

/**
 * Retrieves a dimension object from a domain by index.
 *
 * **Example:**
 *
 * The following retrieves the first dimension from a domain.
 *
 * @code{.c}
 * tiledb_dimension_t* dim;
 * tiledb_domain_get_dimension_from_index(ctx, domain, 0, &dim);
 * @endcode
 *
 * @param ctx The TileDB context
 * @param domain The domain to add the dimension to.
 * @param index The index of domain dimension
 * @param dim The retrieved dimension object.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_domain_get_dimension_from_index(
    tiledb_ctx_t* ctx,
    const tiledb_domain_t* domain,
    uint32_t index,
    tiledb_dimension_t** dim);

/**
 * Retrieves a dimension object from a domain by name (key).
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_dimension_t* dim;
 * tiledb_domain_get_dimension_from_name(ctx, domain, "dim_0", &dim);
 * @endcode
 *
 * @param ctx The TileDB context
 * @param domain The domain to add the dimension to.
 * @param name The name (key) of the requested dimension
 * @param dim The retrieved dimension object.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_domain_get_dimension_from_name(
    tiledb_ctx_t* ctx,
    const tiledb_domain_t* domain,
    const char* name,
    tiledb_dimension_t** dim);

/**
 * Checks whether the domain has a dimension of the given name.
 *
 * **Example:**
 *
 * @code{.c}
 * int32_t has_dim;
 * tiledb_domain_has_dimension(ctx, domain, "dim_0", &has_dim);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param domain The domain.
 * @param name The name of the dimension to check for.
 * @param has_dim Set to `1` if the domain has a dimension of the given name,
 *      else `0`.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_domain_has_dimension(
    tiledb_ctx_t* ctx,
    const tiledb_domain_t* domain,
    const char* name,
    int32_t* has_dim);

/**
 * Dumps the info of a domain in ASCII form to some output (e.g.,
 * file or `stdout`).
 *
 * **Example:**
 *
 * The following prints the domain dump to the standard output.
 *
 * @code{.c}
 * tiledb_domain_dump(ctx, domain, stdout);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param domain The domain.
 * @param out The output.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_domain_dump(tiledb_ctx_t* ctx, const tiledb_domain_t* domain, FILE* out);

/* ********************************* */
/*             DIMENSION             */
/* ********************************* */

/**
 * Creates a dimension.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_dimension_t* dim;
 * int64_t dim_domain[] = {1, 10};
 * int64_t tile_extent = 5;
 * tiledb_dimension_alloc(
 *     ctx, "dim_0", TILEDB_INT64, dim_domain, &tile_extent, &dim);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param name The dimension name.
 * @param type The dimension type.
 * @param dim_domain The dimension domain.
 * @param tile_extent The dimension tile extent.
 * @param dim The dimension to be created.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_dimension_alloc(
    tiledb_ctx_t* ctx,
    const char* name,
    tiledb_datatype_t type,
    const void* dim_domain,
    const void* tile_extent,
    tiledb_dimension_t** dim);

/**
 * Destroys a TileDB dimension, freeing associated memory.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_dimension_free(&dim);
 * @endcode
 *
 * @param dim The dimension to be destroyed.
 */
TILEDB_EXPORT void tiledb_dimension_free(tiledb_dimension_t** dim);

/**
 * Sets the filter list for a dimension.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_filter_list_t* filter_list;
 * tiledb_filter_list_alloc(ctx, &filter_list);
 * tiledb_filter_list_add_filter(ctx, filter_list, filter);
 * tiledb_dimension_set_filter_list(ctx, dim, filter_list);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param dim The target dimension.
 * @param filter_list The filter_list to be set.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_dimension_set_filter_list(
    tiledb_ctx_t* ctx,
    tiledb_dimension_t* dim,
    tiledb_filter_list_t* filter_list);

/**
 * Sets the number of values per cell for a dimension. If this is not
 * used, the default is `1`.
 *
 * **Examples:**
 *
 * For a fixed-sized dimension:
 *
 * @code{.c}
 * tiledb_dimension_set_cell_val_num(ctx, dim, 3);
 * @endcode
 *
 * For a variable-sized dimension:
 *
 * @code{.c}
 * tiledb_dimension_set_cell_val_num(ctx, dim, TILEDB_VAR_NUM);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param dim The target dimension.
 * @param cell_val_num The number of values per cell.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_dimension_set_cell_val_num(
    tiledb_ctx_t* ctx, tiledb_dimension_t* dim, uint32_t cell_val_num);

/**
 * Retrieves the filter list for a dimension.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_filter_list_t* filter_list;
 * tiledb_dimension_get_filter_list(ctx, dim, &filter_list);
 * tiledb_filter_list_free(&filter_list);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param dim The target dimension.
 * @param filter_list The filter list to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_dimension_get_filter_list(
    tiledb_ctx_t* ctx,
    tiledb_dimension_t* dim,
    tiledb_filter_list_t** filter_list);

/**
 * Retrieves the number of values per cell for a dimension. For variable-sized
 * dimensions the result is TILEDB_VAR_NUM.
 *
 * **Example:**
 *
 * @code{.c}
 * uint32_t num;
 * tiledb_dimension_get_cell_val_num(ctx, dim, &num);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param dim The dimension.
 * @param cell_val_num The number of values per cell to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_dimension_get_cell_val_num(
    tiledb_ctx_t* ctx, const tiledb_dimension_t* dim, uint32_t* cell_val_num);

/**
 * Retrieves the dimension name.
 *
 * **Example:**
 *
 * @code{.c}
 * const char* dim_name;
 * tiledb_dimension_get_name(ctx, dim, &dim_name);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param dim The dimension.
 * @param name The name to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_dimension_get_name(
    tiledb_ctx_t* ctx, const tiledb_dimension_t* dim, const char** name);

/**
 * Retrieves the dimension type.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_datatype_t dim_type;
 * tiledb_dimension_get_type(ctx, dim, &dim_type);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param dim The dimension.
 * @param type The type to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_dimension_get_type(
    tiledb_ctx_t* ctx, const tiledb_dimension_t* dim, tiledb_datatype_t* type);

/**
 * Retrieves the domain of the dimension.
 *
 * **Example:**
 *
 * @code{.c}
 * uint64_t* domain;
 * tiledb_dimension_get_domain(ctx, dim, &domain);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param dim The dimension.
 * @param domain The domain to be retrieved. Note that the defined type of
 *     input `domain` must be the same as the dimension type, otherwise the
 *     behavior is unpredictable (it will probably segfault).
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_dimension_get_domain(
    tiledb_ctx_t* ctx, const tiledb_dimension_t* dim, const void** domain);

/**
 * Retrieves the tile extent of the dimension.
 *
 * **Example:**
 *
 * @code{.c}
 * uint64_t* tile_extent;
 * tiledb_dimension_get_tile_extent(ctx, dim, &tile_extent);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param dim The dimension.
 * @param tile_extent The tile extent (pointer) to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_dimension_get_tile_extent(
    tiledb_ctx_t* ctx, const tiledb_dimension_t* dim, const void** tile_extent);

/**
 * Dumps the contents of a dimension in ASCII form to some output (e.g.,
 * file or stdout).
 *
 * **Example:**
 *
 * The following prints the dimension dump to standard output.
 *
 * @code{.c}
 * tiledb_dimension_dump(ctx, dim, stdout);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param dim The dimension.
 * @param out The output.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_dimension_dump(
    tiledb_ctx_t* ctx, const tiledb_dimension_t* dim, FILE* out);

/* ********************************* */
/*            ARRAY SCHEMA           */
/* ********************************* */

/**
 * Creates a TileDB array schema object.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_array_schema_t* array_schema;
 * tiledb_array_schema_alloc(ctx, TILEDB_DENSE, &array_schema);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array_type The array type.
 * @param array_schema The TileDB array schema to be created.
 * @return `TILEDB_OK` for success and `TILEDB_OOM` or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_schema_alloc(
    tiledb_ctx_t* ctx,
    tiledb_array_type_t array_type,
    tiledb_array_schema_t** array_schema);

/**
 * Destroys an array schema, freeing associated memory.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_array_schema_free(&array_schema);
 * @endcode
 *
 * @param array_schema The array schema to be destroyed.
 */
TILEDB_EXPORT void tiledb_array_schema_free(
    tiledb_array_schema_t** array_schema);

/**
 * Adds an attribute to an array schema.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_attribute_t* attr;
 * tiledb_attribute_alloc(ctx, "my_attr", TILEDB_INT32, &attr);
 * tiledb_array_schema_add_attribute(ctx, array_schema, attr);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array_schema The array schema.
 * @param attr The attribute to be added.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_schema_add_attribute(
    tiledb_ctx_t* ctx,
    tiledb_array_schema_t* array_schema,
    tiledb_attribute_t* attr);

/**
 * Sets whether the array can allow coordinate duplicates or not.
 * Applicable only to sparse arrays (it errors out if set to `1` for dense
 * arrays).
 *
 * **Example:**
 *
 * @code{.c}
 * int allows_dups = 1;
 * tiledb_array_schema_set_allows_dups(ctx, array_schema, allows_dups);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array_schema The array schema.
 * @param allows_dups Whether or not the array allows coordinate duplicates.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_schema_set_allows_dups(
    tiledb_ctx_t* ctx, tiledb_array_schema_t* array_schema, int allows_dups);

/**
 * Gets whether the array can allow coordinate duplicates or not.
 * It should always be `0` for dense arrays.
 *
 * **Example:**
 *
 * @code{.c}
 * int allows_dups;
 * tiledb_array_schema_get_allows_dups(ctx, array_schema, &allows_dups);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array_schema The array schema.
 * @param allows_dups Whether or not the array allows coordinate duplicates.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_schema_get_allows_dups(
    tiledb_ctx_t* ctx, tiledb_array_schema_t* array_schema, int* allows_dups);

/**
 * Sets a domain for the array schema.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_domain_t* domain;
 * tiledb_domain_alloc(ctx, &domain);
 * // -- Add dimensions to the domain here -- //
 * tiledb_array_schema_set_domain(ctx, array_schema, domain);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array_schema The array schema.
 * @param domain The domain to be set.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_schema_set_domain(
    tiledb_ctx_t* ctx,
    tiledb_array_schema_t* array_schema,
    tiledb_domain_t* domain);

/**
 * Sets the tile capacity.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_array_schema_set_capacity(ctx, array_schema, 10000);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array_schema The array schema.
 * @param capacity The capacity of a sparse data tile. Note that
 * sparse data tiles exist in sparse fragments, which can be created
 * in both sparse and dense arrays. For more details,
 * see [tutorials/tiling-sparse.html](tutorials/tiling-sparse.html).
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_schema_set_capacity(
    tiledb_ctx_t* ctx, tiledb_array_schema_t* array_schema, uint64_t capacity);

/**
 * Sets the cell order.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_array_schema_set_cell_order(ctx, array_schema, TILEDB_ROW_MAJOR);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array_schema The array schema.
 * @param cell_order The cell order to be set.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_schema_set_cell_order(
    tiledb_ctx_t* ctx,
    tiledb_array_schema_t* array_schema,
    tiledb_layout_t cell_order);

/**
 * Sets the tile order.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_array_schema_set_cell_order(ctx, array_schema, TILEDB_COL_MAJOR);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array_schema The array schema.
 * @param tile_order The tile order to be set.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_schema_set_tile_order(
    tiledb_ctx_t* ctx,
    tiledb_array_schema_t* array_schema,
    tiledb_layout_t tile_order);

/**
 * Sets the filter list to use for the coordinates.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_filter_list_t* filter_list;
 * tiledb_filter_list_alloc(ctx, &filter_list);
 * tiledb_filter_list_add_filter(ctx, filter_list, filter);
 * tiledb_array_schema_set_coords_filter_list(ctx, array_schema, filter_list);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array_schema The array schema.
 * @param filter_list The filter list to be set.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_schema_set_coords_filter_list(
    tiledb_ctx_t* ctx,
    tiledb_array_schema_t* array_schema,
    tiledb_filter_list_t* filter_list);

/**
 * Sets the filter list to use for the offsets of variable-sized attribute
 * values.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_filter_list_t* filter_list;
 * tiledb_filter_list_alloc(ctx, &filter_list);
 * tiledb_filter_list_add_filter(ctx, filter_list, filter);
 * tiledb_array_schema_set_offsets_filter_list(ctx, array_schema, filter_list);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array_schema The array schema.
 * @param filter_list The filter list to be set.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_schema_set_offsets_filter_list(
    tiledb_ctx_t* ctx,
    tiledb_array_schema_t* array_schema,
    tiledb_filter_list_t* filter_list);

/**
 * Checks the correctness of the array schema.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_array_schema_check(ctx, array_schema);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array_schema The array schema.
 * @return `TILEDB_OK` if the array schema is correct and `TILEDB_ERR` upon any
 *     error.
 */
TILEDB_EXPORT int32_t tiledb_array_schema_check(
    tiledb_ctx_t* ctx, tiledb_array_schema_t* array_schema);

/**
 * Retrieves the schema of an array from the disk, creating an array schema
 * struct.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_array_schema_t* array_schema;
 * tiledb_array_schema_load(ctx, "s3://tiledb_bucket/my_array", &array_schema);
 * // Make sure to free the array schema in the end
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array_uri The array whose schema will be retrieved.
 * @param array_schema The array schema to be retrieved, or `NULL` upon error.
 * @return `TILEDB_OK` for success and `TILEDB_OOM` or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_schema_load(
    tiledb_ctx_t* ctx,
    const char* array_uri,
    tiledb_array_schema_t** array_schema);

/**
 * Retrieves the schema of an encrypted array from the disk, creating an array
 * schema struct.
 *
 * **Example:**
 *
 * @code{.c}
 * // Load AES-256 key from disk, environment variable, etc.
 * uint8_t key[32] = ...;
 * tiledb_array_schema_t* array_schema;
 * tiledb_array_schema_load_with_key(
 *     ctx, "s3://tiledb_bucket/my_array", TILEDB_AES_256_GCM,
 *     key, sizeof(key), &array_schema);
 * // Make sure to free the array schema in the end
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array_uri The array whose schema will be retrieved.
 * @param encryption_type The encryption type to use.
 * @param encryption_key The encryption key to use.
 * @param key_length Length in bytes of the encryption key.
 * @param array_schema The array schema to be retrieved, or `NULL` upon error.
 * @return `TILEDB_OK` for success and `TILEDB_OOM` or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_schema_load_with_key(
    tiledb_ctx_t* ctx,
    const char* array_uri,
    tiledb_encryption_type_t encryption_type,
    const void* encryption_key,
    uint32_t key_length,
    tiledb_array_schema_t** array_schema);

/**
 * Retrieves the array type.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_array_schema_t* array_schema;
 * tiledb_array_schema_load(ctx, "s3://tiledb_bucket/my_array", array_schema);
 * tiledb_array_type_t* array_type;
 * tiledb_array_schema_get_array_type(ctx, array_schema, &array_type);
 * // Make sure to free the array schema in the end
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array_schema The array schema.
 * @param array_type The array type to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_schema_get_array_type(
    tiledb_ctx_t* ctx,
    const tiledb_array_schema_t* array_schema,
    tiledb_array_type_t* array_type);

/**
 * Retrieves the capacity.
 *
 * **Example:**
 *
 * @code{.c}
 * uint64_t capacity;
 * tiledb_array_schema_get_capacity(ctx, array_schema, &capacity);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array_schema The array schema.
 * @param capacity The capacity to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_schema_get_capacity(
    tiledb_ctx_t* ctx,
    const tiledb_array_schema_t* array_schema,
    uint64_t* capacity);

/**
 * Retrieves the cell order.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_layout_t cell_order;
 * tiledb_array_schema_get_cell_order(ctx, array_schema, &cell_order);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array_schema The array schema.
 * @param cell_order The cell order to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_schema_get_cell_order(
    tiledb_ctx_t* ctx,
    const tiledb_array_schema_t* array_schema,
    tiledb_layout_t* cell_order);

/**
 * Retrieves the filter list used for the coordinates.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_filter_list_t* filter_list;
 * tiledb_array_schema_get_coords_filter_list(ctx, array_schema, &filter_list);
 * tiledb_filter_list_free(ctx, &filter_list);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array_schema The array schema.
 * @param filter_list The filter list to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_schema_get_coords_filter_list(
    tiledb_ctx_t* ctx,
    tiledb_array_schema_t* array_schema,
    tiledb_filter_list_t** filter_list);

/**
 * Retrieves the filter list used for the offsets.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_filter_list_t* filter_list;
 * tiledb_array_schema_get_offsets_filter_list(ctx, array_schema, &filter_list);
 * tiledb_filter_list_free(ctx, &filter_list);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array_schema The array schema.
 * @param filter_list The filter list to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_schema_get_offsets_filter_list(
    tiledb_ctx_t* ctx,
    tiledb_array_schema_t* array_schema,
    tiledb_filter_list_t** filter_list);

/**
 * Retrieves the array domain.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_domain_t* domain;
 * tiledb_array_schema_get_domain(ctx, array_schema, &domain);
 * // Make sure to delete domain in the end
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array_schema The array schema.
 * @param domain The array domain to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_OOM` or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_schema_get_domain(
    tiledb_ctx_t* ctx,
    const tiledb_array_schema_t* array_schema,
    tiledb_domain_t** domain);

/**
 * Retrieves the tile order.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_layout_t tile_order;
 * tiledb_array_schema_get_tile_order(ctx, array_schema, &tile_order);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array_schema The array schema.
 * @param tile_order The tile order to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_schema_get_tile_order(
    tiledb_ctx_t* ctx,
    const tiledb_array_schema_t* array_schema,
    tiledb_layout_t* tile_order);

/**
 * Retrieves the number of array attributes.
 *
 * **Example:**
 *
 * @code{.c}
 * uint32_t attr_num;
 * tiledb_array_schema_get_attribute_num(ctx, array_schema, &attr_num);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array_schema The array schema.
 * @param attribute_num The number of attributes to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_schema_get_attribute_num(
    tiledb_ctx_t* ctx,
    const tiledb_array_schema_t* array_schema,
    uint32_t* attribute_num);

/**
 * Retrieves an attribute given its index.
 *
 * Attributes are ordered the same way they were defined
 * when constructing the array schema.
 *
 * **Example:**
 *
 * The following retrieves the first attribute in the schema.
 *
 * @code{.c}
 * tiledb_attribute_t* attr;
 * tiledb_array_schema_get_attribute_from_index(ctx, array_schema, 0, &attr);
 * // Make sure to delete the retrieved attribute in the end.
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array_schema The array schema.
 * @param index The index of the attribute to retrieve.
 * @param attr The attribute object to retrieve.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_schema_get_attribute_from_index(
    tiledb_ctx_t* ctx,
    const tiledb_array_schema_t* array_schema,
    uint32_t index,
    tiledb_attribute_t** attr);

/**
 * Retrieves an attribute given its name (key).
 *
 * **Example:**
 *
 * The following retrieves the first attribute in the schema.
 *
 * @code{.c}
 * tiledb_attribute_t* attr;
 * tiledb_array_schema_get_attribute_from_name(
 *     ctx, array_schema, "attr_0", &attr);
 * // Make sure to delete the retrieved attribute in the end.
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array_schema The array schema.
 * @param name The name (key) of the attribute to retrieve.
 * @param attr THe attribute object to retrieve.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_schema_get_attribute_from_name(
    tiledb_ctx_t* ctx,
    const tiledb_array_schema_t* array_schema,
    const char* name,
    tiledb_attribute_t** attr);

/**
 * Checks whether the array schema has an attribute of the given name.
 *
 * **Example:**
 *
 * @code{.c}
 * int32_t has_attr;
 * tiledb_array_schema_has_attribute(ctx, array_schema, "attr_0", &has_attr);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array_schema The array schema.
 * @param name The name of the attribute to check for.
 * @param has_attr Set to `1` if the array schema has an attribute of the
 *      given name, else `0`.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_schema_has_attribute(
    tiledb_ctx_t* ctx,
    const tiledb_array_schema_t* array_schema,
    const char* name,
    int32_t* has_attr);

/**
 * Dumps the array schema in ASCII format in the selected output.
 *
 * **Example:**
 *
 * The following prints the array schema dump in standard output.
 *
 * @code{.c}
 * tiledb_array_schema_dump(ctx, array_schema, stdout);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array_schema The array schema.
 * @param out The output.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_schema_dump(
    tiledb_ctx_t* ctx, const tiledb_array_schema_t* array_schema, FILE* out);

/* ********************************* */
/*               QUERY               */
/* ********************************* */

/**
 * Creates a TileDB query object. Note that the query object is associated
 * with a specific array object. The query type (read or write) is inferred
 * from the array object, which was opened with a specific query type.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_array_t* array;
 * tiledb_array_alloc(ctx, "file:///my_array", &array);
 * tiledb_array_open(ctx, array, TILEDB_WRITE);
 * tiledb_query_t* query;
 * tiledb_query_alloc(ctx, array, TILEDB_WRITE, &query);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param query The query object to be created.
 * @param array An opened array object.
 * @param query_type The query type. This must comply with the query type
 *     `array` was opened.
 * @return `TILEDB_OK` for success and `TILEDB_OOM` or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_query_alloc(
    tiledb_ctx_t* ctx,
    tiledb_array_t* array,
    tiledb_query_type_t query_type,
    tiledb_query_t** query);

/**
 * Retrieves the stats from a Query.
 *
 * **Example:**
 *
 * @code{.c}
 * char* stats_json;
 * tiledb_query_get_stats(ctx, query, &stats_json);
 * // Make sure to free the retrieved `stats_json`
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param query The query object.
 * @param stats_json The output json. The caller takes ownership
 *   of the c-string.
 * @return `TILEDB_OK` for success and `TILEDB_OOM` or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_query_get_stats(
    tiledb_ctx_t* ctx, tiledb_query_t* query, char** stats_json);

/**
 * Set the query config
 *
 * Setting the configuration with this function overrides the following
 * Query-level parameters only:
 *
 * - `sm.memory_budget`
 * - `sm.memory_budget_var`
 * - `sm.sub_partitioner_memory_budget`
 * - `sm.var_offsets.mode`
 * - `sm.var_offsets.extra_element`
 * - `sm.var_offsets.bitsize`
 * - `sm.check_coord_dups`
 * - `sm.check_coord_oob`
 * - `sm.check_global_order`
 * - `sm.dedup_coords`
 */
TILEDB_EXPORT int32_t tiledb_query_set_config(
    tiledb_ctx_t* ctx, tiledb_query_t* query, tiledb_config_t* config);

/**
 * Retrieves the config from a Query.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_config_t* config;
 * tiledb_query_get_config(ctx, vfs, &config);
 * // Make sure to free the retrieved config
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param query The query object.
 * @param config The config to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_OOM` or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_query_get_config(
    tiledb_ctx_t* ctx, tiledb_query_t* query, tiledb_config_t** config);
/**
 * Indicates that the query will write or read a subarray, and provides
 * the appropriate information.
 *
 * **Example:**
 *
 * The following sets a 2D subarray [0,10], [20, 30] to the query.
 *
 * @code{.c}
 * uint64_t subarray[] = { 0, 10, 20, 30};
 * tiledb_query_set_subarray(ctx, query, subarray);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param query The TileDB query.
 * @param subarray The subarray in which the array read/write will be
 *     constrained on. It should be a sequence of [low, high] pairs (one
 *     pair per dimension). For the case of writes, this is meaningful only
 *     for dense arrays, and specifically dense writes. Note that `subarray`
 *     must have the same type as the domain.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 *
 * @note If you set the subarray of a completed, incomplete or in-progress
 *     query, this function will clear the internal state and render it
 *     as uninitialized. However, the potentially set layout and attribute
 *     buffers will be retained. This is useful when the user wishes to
 *     fix the attributes and layout, but explore different subarrays with
 *     the same `tiledb_query_t` object (i.e., without having to create
 *     a new object).
 *
 * @note Setting the subarray in sparse writes is meaningless and, thus,
 *     this function will error in the following two cases, provided that
 *     this is a write query:
 *     (i) the array is sparse, and (ii) the array is dense and the
 *     layout has been set to `TILEDB_UNORDERED`. In the second case,
 *     if the user sets the layout to `TILEDB_UNORDERED` **after**
 *     the subarray has been set, the subarray will simply be ignored.
 */
TILEDB_EXPORT int32_t tiledb_query_set_subarray(
    tiledb_ctx_t* ctx, tiledb_query_t* query, const void* subarray);

/**
 * Sets the buffer for a fixed-sized attribute/dimension to a query, which will
 * either hold the values to be written (if it is a write query), or will hold
 * the results from a read query.
 *
 * **Example:**
 *
 * @code{.c}
 * int32_t a1[100];
 * uint64_t a1_size = sizeof(a1);
 * tiledb_query_set_buffer(ctx, query, "a1", a1, &a1_size);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param query The TileDB query.
 * @param name The attribute/dimension to set the buffer for. Note that
 *     zipped coordinates have special name `TILEDB_COORDS`.
 * @param buffer The buffer that either have the input data to be written,
 *     or will hold the data to be read.
 * @param buffer_size In the case of writes, this is the size of `buffer`
 *     in bytes. In the case of reads, this initially contains the allocated
 *     size of `buffer`, but after the termination of the query
 *     it will contain the size of the useful (read) data in `buffer`.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_query_set_buffer(
    tiledb_ctx_t* ctx,
    tiledb_query_t* query,
    const char* name,
    void* buffer,
    uint64_t* buffer_size);

/**
 * Sets the buffer for a var-sized attribute/dimension to a query, which will
 * either hold the values to be written (if it is a write query), or will hold
 * the results from a read query.
 *
 * **Example:**
 *
 * @code{.c}
 * uint64_t a2_off[10];
 * uint64_t a2_off_size = sizeof(a2_off);
 * char a2_val[100];
 * uint64_t a2_val_size = sizeof(a2_val);
 * tiledb_query_set_buffer_var(
 *     ctx, query, "a2", a2_off, &a2_off_size, a2_val, &a2_val_size);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param query The TileDB query.
 * @param name The attribute/dimension to set the buffer for.
 * @param buffer_off The buffer that either have the input data to be written,
 *     or will hold the data to be read. This buffer holds the starting offsets
 *     of each cell value in `buffer_val`.
 * @param buffer_off_size In the case of writes, it is the size of `buffer_off`
 *     in bytes. In the case of reads, this initially contains the allocated
 *     size of `buffer_off`, but after the *end of the query*
 *     (`tiledb_query_submit`) it will contain the size of the useful (read)
 *     data in `buffer_off`.
 * @param buffer_val The buffer that either have the input data to be written,
 *     or will hold the data to be read. This buffer holds the actual var-sized
 *     cell values.
 * @param buffer_val_size In the case of writes, it is the size of `buffer_val`
 *     in bytes. In the case of reads, this initially contains the allocated
 *     size of `buffer_val`, but after the termination of the function
 *     it will contain the size of the useful (read) data in `buffer_val`.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_query_set_buffer_var(
    tiledb_ctx_t* ctx,
    tiledb_query_t* query,
    const char* name,
    uint64_t* buffer_off,
    uint64_t* buffer_off_size,
    void* buffer_val,
    uint64_t* buffer_val_size);

/**
 * Sets the buffer for a fixed-sized, nullable attribute to a query, which will
 * either hold the values to be written (if it is a write query), or will hold
 * the results from a read query. The validity buffer is a byte map, where each
 * non-zero byte represents a valid (i.e. "non-null") attribute value.
 *
 * **Example:**
 *
 * @code{.c}
 * int32_t a1[100];
 * uint64_t a1_size = sizeof(a1);
 * uint8_t a1_validity[100];
 * uint64_t a1_validity_size = sizeof(a1_validity);
 * tiledb_query_set_buffer_nullable(
 *   ctx, query, "a1", a1, &a1_size, a1_validity, &a1_validity_size);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param query The TileDB query.
 * @param name The attribute/dimension to set the buffer for. Note that
 *     zipped coordinates have special name `TILEDB_COORDS`.
 * @param buffer The buffer that either have the input data to be written,
 *     or will hold the data to be read.
 * @param buffer_size In the case of writes, this is the size of `buffer`
 *     in bytes. In the case of reads, this initially contains the allocated
 *     size of `buffer`, but after the termination of the query
 *     it will contain the size of the useful (read) data in `buffer`.
 * @param buffer_validity_bytemap The validity byte map that has exactly
 *     one value for each value in `buffer`.
 * @param buffer_validity_bytemap_size In the case of writes, this is the
 *     size of `buffer_validity_bytemap` in bytes. In the case of reads,
 *     this initially contains the allocated size of `buffer_validity_bytemap`,
 *     but after the termination of the query it will contain the size of the
 *     useful (read) data in `buffer_validity_bytemap`.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_query_set_buffer_nullable(
    tiledb_ctx_t* ctx,
    tiledb_query_t* query,
    const char* name,
    void* buffer,
    uint64_t* buffer_size,
    uint8_t* buffer_validity_bytemap,
    uint64_t* buffer_validity_bytemap_size);

/**
 * Sets the buffer for a var-sized, nullable attribute to a query, which will
 * either hold the values to be written (if it is a write query), or will hold
 * the results from a read query.
 *
 * **Example:**
 *
 * @code{.c}
 * uint64_t a2_off[10];
 * uint64_t a2_off_size = sizeof(a2_off);
 * char a2_val[100];
 * uint64_t a2_val_size = sizeof(a2_val);
 * uint8_t a2_validity[100];
 * uint64_t a2_validity_size = sizeof(a2_validity);
 * tiledb_query_set_buffer_var(
 *     ctx, query, "a2", a2_off, &a2_off_size, a2_val, &a2_val_size,
 *     a2_validity, &a2_validity_size);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param query The TileDB query.
 * @param name The attribute/dimension to set the buffer for.
 * @param buffer_off The buffer that either have the input data to be written,
 *     or will hold the data to be read. This buffer holds the starting offsets
 *     of each cell value in `buffer_val`.
 * @param buffer_off_size In the case of writes, it is the size of `buffer_off`
 *     in bytes. In the case of reads, this initially contains the allocated
 *     size of `buffer_off`, but after the *end of the query*
 *     (`tiledb_query_submit`) it will contain the size of the useful (read)
 *     data in `buffer_off`.
 * @param buffer_val The buffer that either have the input data to be written,
 *     or will hold the data to be read. This buffer holds the actual var-sized
 *     cell values.
 * @param buffer_val_size In the case of writes, it is the size of `buffer_val`
 *     in bytes. In the case of reads, this initially contains the allocated
 *     size of `buffer_val`, but after the termination of the function
 *     it will contain the size of the useful (read) data in `buffer_val`.
 * @param buffer_validity_bytemap The validity byte map that has exactly
 *     one value for each value in `buffer`.
 * @param buffer_validity_bytemap_size In the case of writes, this is the
 *     size of `buffer_validity_bytemap` in bytes. In the case of reads,
 *     this initially contains the allocated size of `buffer_validity_bytemap`,
 *     but after the termination of the query it will contain the size of the
 *     useful (read) data in `buffer_validity_bytemap`.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_query_set_buffer_var_nullable(
    tiledb_ctx_t* ctx,
    tiledb_query_t* query,
    const char* name,
    uint64_t* buffer_off,
    uint64_t* buffer_off_size,
    void* buffer_val,
    uint64_t* buffer_val_size,
    uint8_t* buffer_validity_bytemap,
    uint64_t* buffer_validity_bytemap_size);

/**
 * Gets the buffer of a fixed-sized attribute/dimension from a query. If the
 * buffer has not been set, then `buffer` is set to `nullptr`.
 *
 * **Example:**
 *
 * @code{.c}
 * int* a1;
 * uint64_t* a1_size;
 * tiledb_query_get_buffer(ctx, query, "a1", &a1, &a1_size);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param query The TileDB query.
 * @param name The attribute/dimension to get the buffer for. Note that the
 *     zipped coordinates have special name `TILEDB_COORDS`.
 * @param buffer The buffer to retrieve.
 * @param buffer_size A pointer to the size of the buffer. Note that this is
 *     a double pointer and returns the original variable address from
 *     `set_buffer`.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_query_get_buffer(
    tiledb_ctx_t* ctx,
    tiledb_query_t* query,
    const char* name,
    void** buffer,
    uint64_t** buffer_size);

/**
 * Gets the values and offsets buffers for a var-sized attribute/dimension
 * to a query. If the buffers have not been set, then `buffer_off` and
 * `buffer_val` are set to `nullptr`.
 *
 * **Example:**
 *
 * @code{.c}
 * uint64_t* a2_off;
 * uint64_t* a2_off_size;
 * char* a2_val;
 * uint64_t* a2_val_size;
 * tiledb_query_get_buffer_var(
 *     ctx, query, "a2", &a2_off, &a2_off_size, &a2_val, &a2_val_size);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param query The TileDB query.
 * @param name The attribute/dimension to set the buffer for.
 * @param buffer_off The offsets buffer to be retrieved.
 * @param buffer_off_size A pointer to the size of the offsets buffer. Note that
 *     this is a `uint_64**` pointer and returns the original variable address
 * from `set_buffer`.
 * @param buffer_val The values buffer to be retrieved.
 * @param buffer_val_size A pointer to the size of the values buffer. Note that
 *     this is a `uint_64**` pointer and returns the original variable address
 * from `set_buffer`.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_query_get_buffer_var(
    tiledb_ctx_t* ctx,
    tiledb_query_t* query,
    const char* name,
    uint64_t** buffer_off,
    uint64_t** buffer_off_size,
    void** buffer_val,
    uint64_t** buffer_val_size);

/**
 * Gets the buffer of a fixed-sized, nullable attribute from a query. If the
 * buffer has not been set, then `buffer` and `buffer_validity_bytemap` are
 * set to `nullptr`.
 *
 * **Example:**
 *
 * @code{.c}
 * int* a1;
 * uint64_t* a1_size;
 * uint8_t* a1_validity;
 * uint64_t* a1_validity_size;
 * tiledb_query_get_buffer(
 *   ctx, query, "a1", &a1, &a1_size, &a1_validity, &a1_validity_size);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param query The TileDB query.
 * @param name The attribute/dimension to get the buffer for. Note that the
 *     zipped coordinates have special name `TILEDB_COORDS`.
 * @param buffer The buffer to retrieve.
 * @param buffer_size A pointer to the size of the buffer. Note that this is
 *     a double pointer and returns the original variable address from
 *     `set_buffer`.
 * @param buffer_validity_bytemap The validity bytemap buffer to retrieve.
 * @param buffer_validity_bytemap_size A pointer to the size of the validity
 *     bytemap buffer. Note that this is a double pointer and returns the
 * origina variable address from `set_buffer_nullable`.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_query_get_buffer_nullable(
    tiledb_ctx_t* ctx,
    tiledb_query_t* query,
    const char* name,
    void** buffer,
    uint64_t** buffer_size,
    uint8_t** buffer_validity_bytemap,
    uint64_t** buffer_validity_bytemap_size);

/**
 * Gets the values and offsets buffers for a var-sized, nullable attribute
 * to a query. If the buffers have not been set, then `buffer_off`,
 * `buffer_val`, and `buffer_validity_bytemap` are set to `nullptr`.
 *
 * **Example:**
 *
 * @code{.c}
 * uint64_t* a2_off;
 * uint64_t* a2_off_size;
 * char* a2_val;
 * uint64_t* a2_val_size;
 * uint8_t* a2_validity;
 * uint64_t* a2_validity_size;
 * tiledb_query_get_buffer_var(
 *     ctx, query, "a2", &a2_off, &a2_off_size, &a2_val, &a2_val_size,
 *     &a2_validity, &a2_validity_size);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param query The TileDB query.
 * @param name The attribute/dimension to set the buffer for.
 * @param buffer_off The offsets buffer to be retrieved.
 * @param buffer_off_size A pointer to the size of the offsets buffer. Note that
 *     this is a `uint_64**` pointer and returns the original variable address
 * from `set_buffer`.
 * @param buffer_val The values buffer to be retrieved.
 * @param buffer_val_size A pointer to the size of the values buffer. Note that
 *     this is a `uint_64**` pointer and returns the original variable address
 * from `set_buffer`.
 * @param buffer_validity_bytemap The validity bytemap buffer to retrieve.
 * @param buffer_validity_bytemap_size A pointer to the size of the validity
 *     bytemap buffer. Note that this is a double pointer and returns the
 * origina variable address from `set_buffer_var_nullable`.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_query_get_buffer_var_nullable(
    tiledb_ctx_t* ctx,
    tiledb_query_t* query,
    const char* name,
    uint64_t** buffer_off,
    uint64_t** buffer_off_size,
    void** buffer_val,
    uint64_t** buffer_val_size,
    uint8_t** buffer_validity_bytemap,
    uint64_t** buffer_validity_bytemap_size);

/**
 * Sets the layout of the cells to be written or read.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_query_set_layout(ctx, query, TILEDB_ROW_MAJOR);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param query The TileDB query.
 * @param layout For a write query, this specifies the order of the cells
 *     provided by the user in the buffers. For a read query, this specifies
 *     the order of the cells that will be retrieved as results and stored
 *     in the user buffers. The layout can be one of the following:
 *    - `TILEDB_COL_MAJOR`:
 *      This means column-major order with respect to the subarray.
 *    - `TILEDB_ROW_MAJOR`:
 *      This means row-major order with respect to the subarray.
 *    - `TILEDB_GLOBAL_ORDER`:
 *      This means that cells are stored or retrieved in the array global
 *      cell order.
 *    - `TILEDB_UNORDERED`:
 *      This is applicable only to reads and writes for sparse arrays, or for
 *      sparse writes to dense arrays. For writes, it specifies that the cells
 *      are unordered and, hence, TileDB must sort the cells in the global cell
 *      order prior to writing. For reads, TileDB will return the cells without
 *      any particular order, which will often lead to better performance.
 * * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_query_set_layout(
    tiledb_ctx_t* ctx, tiledb_query_t* query, tiledb_layout_t layout);

/**
 * Sets the query condition to be applied on a read.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_query_condition_t* query_condition;
 * tiledb_query_condition_alloc(ctx, &query_condition);
 * uint32_t value = 5;
 * tiledb_query_condition_init(
 *   ctx, query_condition, "longitude", &value, sizeof(value), TILEDB_LT);
 * tiledb_query_set_condition(ctx, query, query_condition);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param query The TileDB query.
 * @param cond The TileDB query condition.
 */
TILEDB_EXPORT int32_t tiledb_query_set_condition(
    tiledb_ctx_t* ctx,
    tiledb_query_t* query,
    const tiledb_query_condition_t* cond);

/**
 * Flushes all internal state of a query object and finalizes the query.
 * This is applicable only to global layout writes. It has no effect for
 * any other query type.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_query_t* query;
 * // ... Your code here ... //
 * tiledb_query_finalize(ctx, query);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param query The query object to be flushed.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_query_finalize(tiledb_ctx_t* ctx, tiledb_query_t* query);

/**
 * Frees a TileDB query object.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_query_free(&query);
 * @endcode
 *
 * @param query The query object to be deleted.
 */
TILEDB_EXPORT void tiledb_query_free(tiledb_query_t** query);

/**
 * Submits a TileDB query.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_query_submit(ctx, query);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param query The query to be submitted.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 *
 * @note `tiledb_query_finalize` must be invoked after finish writing in
 *     global layout (via repeated invocations of `tiledb_query_submit`),
 *     in order to flush any internal state.
 *
 * @note For the case of reads, if the returned status is `TILEDB_INCOMPLETE`,
 *    TileDB could not fit the entire result in the user's buffers. In this
 *    case, the user should consume the read results (if any), optionally
 *    reset the buffers with `tiledb_query_set_buffer`, and then resubmit the
 *    query until the status becomes `TILEDB_COMPLETED`. If all buffer sizes
 *    after the termination of this function become 0, then this means that
 *    **no** useful data was read into the buffers, implying that larger
 *    buffers are needed for the query to proceed. In this case, the users
 *    must reallocate their buffers (increasing their size), reset the buffers
 *    with `tiledb_query_set_buffer`, and resubmit the query.
 */
TILEDB_EXPORT int32_t
tiledb_query_submit(tiledb_ctx_t* ctx, tiledb_query_t* query);

/**
 * Submits a TileDB query in asynchronous mode.
 *
 * **Examples:**
 *
 * Submit without a callback.
 *
 * @code{.c}
 * tiledb_query_submit_async(ctx, query, NULL, NULL);
 * @endcode
 *
 * Submit with a callback function `print` that takes as input message
 * `msg` and prints it upon completion of the query.
 *
 * @code{.c}
 * const char* msg = "Query completed";
 * tiledb_query_submit_async(ctx, &query, foo, msg);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param query The query to be submitted.
 * @param callback The function to be called when the query completes.
 * @param callback_data The data to be passed to the callback function.
 * @return `TILEDB_OK` for success and `TILEDB_OOM` or `TILEDB_ERR` for error.
 *
 * @note `tiledb_query_finalize` must be invoked after finish writing in
 *     global layout (via repeated invocations of `tiledb_query_submit`),
 *     in order to flush any internal state.
 *
 * @note For the case of reads, if the returned status is `TILEDB_INCOMPLETE`,
 *    TileDB could not fit the entire result in the user's buffers. In this
 *    case, the user should consume the read results (if any), optionally
 *    reset the buffers with `tiledb_query_set_buffer`, and then resubmit the
 *    query until the status becomes `TILEDB_COMPLETED`. If all buffer sizes
 *    after the termination of this function become 0, then this means that
 *    **no** useful data was read into the buffers, implying that larger
 *    buffers are needed for the query to proceed. In this case, the users
 *    must reallocate their buffers (increasing their size), reset the buffers
 *    with `tiledb_query_set_buffer`, and resubmit the query.
 */
TILEDB_EXPORT int32_t tiledb_query_submit_async(
    tiledb_ctx_t* ctx,
    tiledb_query_t* query,
    void (*callback)(void*),
    void* callback_data);

/**
 * Checks if the query has returned any results. Applicable only to
 * read queries; it sets `has_results` to `0 in the case of writes.
 *
 * **Example:**
 *
 * @code{.c}
 * int32_t has_results;
 * tiledb_query_has_results(ctx, query, &has_results);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param query The query.
 * @param has_results Set to `1` if the query returned results and `0`
 *     otherwise.
 * @return `TILEDB_OK` upon success, and `TILEDB_ERR` upon error.
 */
TILEDB_EXPORT int32_t tiledb_query_has_results(
    tiledb_ctx_t* ctx, tiledb_query_t* query, int32_t* has_results);

/**
 * Retrieves the status of a query.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_query_status_t status;
 * tiledb_query_get_status(ctx, query, &status);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param query The query.
 * @param status The query status to be retrieved.
 * @return `TILEDB_OK` upon success, and `TILEDB_ERR` upon error.
 */
TILEDB_EXPORT int32_t tiledb_query_get_status(
    tiledb_ctx_t* ctx, tiledb_query_t* query, tiledb_query_status_t* status);

/**
 * Retrieves the query type.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_query_type_t query_type;
 * tiledb_query_get_status(ctx, query, &query_type);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param query The query.
 * @param query_type The query type to be retrieved.
 * @return `TILEDB_OK` upon success, and `TILEDB_ERR` upon error.
 */
TILEDB_EXPORT int32_t tiledb_query_get_type(
    tiledb_ctx_t* ctx, tiledb_query_t* query, tiledb_query_type_t* query_type);

/**
 * Retrieves the query layout.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_layout_t query_layout;
 * tiledb_query_get_layout(ctx, query, &query_layout);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param query The query.
 * @param query_layout The query layout to be retrieved.
 * @return `TILEDB_OK` upon success, and `TILEDB_ERR` upon error.
 */
TILEDB_EXPORT int32_t tiledb_query_get_layout(
    tiledb_ctx_t* ctx, tiledb_query_t* query, tiledb_layout_t* query_layout);

/**
 * Retrieves the query array.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_array_t* array;
 * tiledb_query_get_array(ctx, query, &array);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param query The query.
 * @param array The query array to be retrieved.
 * @return `TILEDB_OK` upon success, and `TILEDB_ERR` upon error.
 */
TILEDB_EXPORT int32_t tiledb_query_get_array(
    tiledb_ctx_t* ctx, tiledb_query_t* query, tiledb_array_t** array);
/**
 * Adds a 1D range along a subarray dimension index, which is in the form
 * (start, end, stride). The datatype of the range components
 * must be the same as the type of the domain of the array in the query.
 *
 * **Example:**
 *
 * @code{.c}
 * uint32_t dim_idx = 2;
 * int64_t start = 10;
 * int64_t end = 20;
 * tiledb_query_add_range(ctx, query, dim_idx, &start, &end, nullptr);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param query The query to add the range to.
 * @param dim_idx The index of the dimension to add the range to.
 * @param start The range start.
 * @param end The range end.
 * @param stride The range stride.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 *
 * @note The stride is currently unsupported. Use `nullptr` as the
 *     stride argument.
 */
TILEDB_EXPORT int32_t tiledb_query_add_range(
    tiledb_ctx_t* ctx,
    tiledb_query_t* query,
    uint32_t dim_idx,
    const void* start,
    const void* end,
    const void* stride);

/**
 * Adds a 1D range along a subarray dimension name, which is in the form
 * (start, end, stride). The datatype of the range components
 * must be the same as the type of the domain of the array in the query.
 *
 * **Example:**
 *
 * @code{.c}
 * uint32_t dim_name = "rows";
 * int64_t start = 10;
 * int64_t end = 20;
 * tiledb_query_add_range_by_name(ctx, query, dim_name, &start, &end, nullptr);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param query The query to add the range to.
 * @param dim_name The name of the dimension to add the range to.
 * @param start The range start.
 * @param end The range end.
 * @param stride The range stride.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 *
 * @note The stride is currently unsupported. Use `nullptr` as the
 *     stride argument.
 */
TILEDB_EXPORT int32_t tiledb_query_add_range_by_name(
    tiledb_ctx_t* ctx,
    tiledb_query_t* query,
    const char* dim_name,
    const void* start,
    const void* end,
    const void* stride);

/**
 * Adds a 1D variable-sized range along a subarray dimension index, which is in
 * the form (start, end). Applicable only to variable-sized dimensions.
 *
 * **Example:**
 *
 * @code{.c}
 * uint32_t dim_idx = 2;
 * char start[] = "a";
 * char end[] = "bb";
 * tiledb_query_add_range_var(ctx, query, dim_idx, start, 1, end, 2);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param query The query to add the range to.
 * @param dim_idx The index of the dimension to add the range to.
 * @param start The range start.
 * @param start_size The size of the range start in bytes.
 * @param end The range end.
 * @param end_size The size of the range end in bytes.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_query_add_range_var(
    tiledb_ctx_t* ctx,
    tiledb_query_t* query,
    uint32_t dim_idx,
    const void* start,
    uint64_t start_size,
    const void* end,
    uint64_t end_size);

/**
 * Adds a 1D variable-sized range along a subarray dimension name, which is in
 * the form (start, end). Applicable only to variable-sized dimensions.
 *
 * **Example:**
 *
 * @code{.c}
 * uint32_t dim_name = "rows";
 * char start[] = "a";
 * char end[] = "bb";
 * tiledb_query_add_range_var_by_name(ctx, query, dim_name, start, 1, end, 2);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param query The query to add the range to.
 * @param dim_name The name of the dimension to add the range to.
 * @param start The range start.
 * @param start_size The size of the range start in bytes.
 * @param end The range end.
 * @param end_size The size of the range end in bytes.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_query_add_range_var_by_name(
    tiledb_ctx_t* ctx,
    tiledb_query_t* query,
    const char* dim_name,
    const void* start,
    uint64_t start_size,
    const void* end,
    uint64_t end_size);

/**
 * Retrieves the number of ranges of the query subarray along a given dimension
 * index.
 *
 * **Example:**
 *
 * @code{.c}
 * uint64_t range_num;
 * tiledb_query_get_range_num(ctx, query, dim_idx, &range_num);
 * @endcode
 *
 * @param ctx The TileDB context
 * @param query The query.
 * @param dim_idx The index of the dimension whose range number to retrieve.
 * @param range_num The number of ranges to retrieve.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_query_get_range_num(
    tiledb_ctx_t* ctx,
    const tiledb_query_t* query,
    uint32_t dim_idx,
    uint64_t* range_num);

/**
 * Retrieves the number of ranges of the query subarray along a given dimension
 * name.
 *
 * **Example:**
 *
 * @code{.c}
 * uint64_t range_num;
 * tiledb_query_get_range_num_from_name(ctx, query, dim_name, &range_num);
 * @endcode
 *
 * @param ctx The TileDB context
 * @param query The query.
 * @param dim_name The name of the dimension whose range number to retrieve.
 * @param range_num The number of ranges to retrieve.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_query_get_range_num_from_name(
    tiledb_ctx_t* ctx,
    const tiledb_query_t* query,
    const char* dim_name,
    uint64_t* range_num);

/**
 * Retrieves a specific range of the query subarray along a given dimension
 * index.
 *
 * **Example:**
 *
 * @code{.c}
 * const void* start;
 * const void* end;
 * const void* stride;
 * tiledb_query_get_range(
 *     ctx, query, dim_idx, range_idx, &start, &end, &stride);
 * @endcode
 *
 * @param ctx The TileDB context
 * @param query The query.
 * @param dim_idx The index of the dimension to retrieve the range from.
 * @param range_idx The index of the range to retrieve.
 * @param start The range start to retrieve.
 * @param end The range end to retrieve.
 * @param stride The range stride to retrieve.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_query_get_range(
    tiledb_ctx_t* ctx,
    const tiledb_query_t* query,
    uint32_t dim_idx,
    uint64_t range_idx,
    const void** start,
    const void** end,
    const void** stride);

/**
 * Retrieves a specific range of the query subarray along a given dimension
 * name.
 *
 * **Example:**
 *
 * @code{.c}
 * const void* start;
 * const void* end;
 * const void* stride;
 * tiledb_query_get_range_from_name(
 *     ctx, query, dim_name, range_idx, &start, &end, &stride);
 * @endcode
 *
 * @param ctx The TileDB context
 * @param query The query.
 * @param dim_name The name of the dimension to retrieve the range from.
 * @param range_idx The index of the range to retrieve.
 * @param start The range start to retrieve.
 * @param end The range end to retrieve.
 * @param stride The range stride to retrieve.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_query_get_range_from_name(
    tiledb_ctx_t* ctx,
    const tiledb_query_t* query,
    const char* dim_name,
    uint64_t range_idx,
    const void** start,
    const void** end,
    const void** stride);

/**
 * Retrieves a range's start and end size for a given variable-length
 * dimension index at a given range index.
 *
 * **Example:**
 *
 * @code{.c}
 * uint64_t start_size;
 * uint64_t end_size;
 * tiledb_query_get_range_var_size(
 *     ctx, query, dim_idx, range_idx, &start_size, &end_size);
 * @endcode
 *
 * @param ctx The TileDB context
 * @param query The query.
 * @param dim_idx The index of the dimension to retrieve the range from.
 * @param range_idx The index of the range to retrieve.
 * @param start_size range start size in bytes
 * @param end_size range end size in bytes
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_query_get_range_var_size(
    tiledb_ctx_t* ctx,
    const tiledb_query_t* query,
    uint32_t dim_idx,
    uint64_t range_idx,
    uint64_t* start_size,
    uint64_t* end_size);

/**
 * Retrieves a range's start and end size for a given variable-length
 * dimension name at a given range index.
 *
 * **Example:**
 *
 * @code{.c}
 * uint64_t start_size;
 * uint64_t end_size;
 * tiledb_query_get_range_var_size_from_name(
 *     ctx, query, dim_name, range_idx, &start_size, &end_size);
 * @endcode
 *
 * @param ctx The TileDB context
 * @param query The query.
 * @param dim_name The name of the dimension to retrieve the range from.
 * @param range_idx The index of the range to retrieve.
 * @param start_size range start size in bytes
 * @param end_size range end size in bytes
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_query_get_range_var_size_from_name(
    tiledb_ctx_t* ctx,
    const tiledb_query_t* query,
    const char* dim_name,
    uint64_t range_idx,
    uint64_t* start_size,
    uint64_t* end_size);

/**
 * Retrieves a specific range of the query subarray along a given
 * variable-length dimension index.
 *
 * **Example:**
 *
 * @code{.c}
 * const void* start;
 * const void* end;
 * tiledb_query_get_range_var(
 *     ctx, query, dim_idx, range_idx, &start, &end);
 * @endcode
 *
 * @param ctx The TileDB context
 * @param query The query.
 * @param dim_idx The index of the dimension to retrieve the range from.
 * @param range_idx The index of the range to retrieve.
 * @param start The range start to retrieve.
 * @param end The range end to retrieve.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_query_get_range_var(
    tiledb_ctx_t* ctx,
    const tiledb_query_t* query,
    uint32_t dim_idx,
    uint64_t range_idx,
    void* start,
    void* end);

/**
 * Retrieves a specific range of the query subarray along a given
 * variable-length dimension name.
 *
 * **Example:**
 *
 * @code{.c}
 * const void* start;
 * const void* end;
 * tiledb_query_get_range_var_from_name(
 *     ctx, query, dim_name, range_idx, &start, &end);
 * @endcode
 *
 * @param ctx The TileDB context
 * @param query The query.
 * @param dim_name The name of the dimension to retrieve the range from.
 * @param range_idx The index of the range to retrieve.
 * @param start The range start to retrieve.
 * @param end The range end to retrieve.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_query_get_range_var_from_name(
    tiledb_ctx_t* ctx,
    const tiledb_query_t* query,
    const char* dim_name,
    uint64_t range_idx,
    void* start,
    void* end);

/**
 * Retrieves the estimated result size for a fixed-sized attribute/dimension.
 *
 * **Example:**
 *
 * @code{.c}
 * uint64_t size;
 * tiledb_query_get_est_result_size(ctx, query, "a", &size);
 * @endcode
 *
 * @param ctx The TileDB context
 * @param query The query.
 * @param name The attribute/dimension name.
 * @param size The size (in bytes) to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_query_get_est_result_size(
    tiledb_ctx_t* ctx,
    const tiledb_query_t* query,
    const char* name,
    uint64_t* size);

/**
 * Retrieves the estimated result size for a var-sized attribute/dimension.
 *
 * **Example:**
 *
 * @code{.c}
 * uint64_t size_off, size_val;
 * tiledb_query_get_est_result_size_var(
 *     ctx, query, "a", &size_off, &size_val);
 * @endcode
 *
 * @param ctx The TileDB context
 * @param query The query.
 * @param name The attribute/dimension name.
 * @param size_off The size of the offsets (in bytes) to be retrieved.
 * @param size_val The size of the values (in bytes) to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_query_get_est_result_size_var(
    tiledb_ctx_t* ctx,
    const tiledb_query_t* query,
    const char* name,
    uint64_t* size_off,
    uint64_t* size_val);

/**
 * Retrieves the estimated result size for a fixed-sized, nullable attribute.
 *
 * **Example:**
 *
 * @code{.c}
 * uint64_t size_val;
 * uint64_t size_validity;
 * tiledb_query_get_est_result_size_nullable(ctx, query, "a", &size_val,
 * &size_validity);
 * @endcode
 *
 * @param ctx The TileDB context
 * @param query The query.
 * @param name The attribute name.
 * @param size_val The size of the values (in bytes) to be retrieved.
 * @param size_validity The size of the validity values (in bytes) to be
 * retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_query_get_est_result_size_nullable(
    tiledb_ctx_t* ctx,
    const tiledb_query_t* query,
    const char* name,
    uint64_t* size_val,
    uint64_t* size_validity);

/**
 * Retrieves the estimated result size for a var-sized, nullable attribute.
 *
 * **Example:**
 *
 * @code{.c}
 * uint64_t size_off, size_val, size_validity;
 * tiledb_query_get_est_result_size_var_nullable(
 *     ctx, query, "a", &size_off, &size_val, &size_validity);
 * @endcode
 *
 * @param ctx The TileDB context
 * @param query The query.
 * @param name The attribute name.
 * @param size_off The size of the offsets (in bytes) to be retrieved.
 * @param size_val The size of the values (in bytes) to be retrieved.
 * @param size_validity The size of the validity values (in bytes) to be
 * retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_query_get_est_result_size_var_nullable(
    tiledb_ctx_t* ctx,
    const tiledb_query_t* query,
    const char* name,
    uint64_t* size_off,
    uint64_t* size_val,
    uint64_t* size_validity);

/**
 * Retrieves the number of written fragments. Applicable only to WRITE
 * queries.
 *
 * **Example:**
 *
 * @code{.c}
 * uint32_t num;
 * tiledb_query_get_fragment_num(ctx, query, &num);
 * @endcode
 *
 * @param ctx The TileDB context
 * @param query The query.
 * @param num The number of written fragments to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_query_get_fragment_num(
    tiledb_ctx_t* ctx, const tiledb_query_t* query, uint32_t* num);

/**
 * Retrieves the URI of the written fragment with the input index. Applicable
 * only to WRITE queries.
 *
 * **Example:**
 *
 * @code{.c}
 * const char* uri;
 * tiledb_query_get_fragment_uri(
 *     ctx, query, 0, &uri);
 * @endcode
 *
 * @param ctx The TileDB context
 * @param query The query.
 * @param idx The index of the written fragment.
 * @param uri The URI of the written fragment to be returned.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 *
 * @note Make sure to make a copy of `uri` after its retrieval, as the
 *     constant pointer may be updated internally as new fragments
 *     are being written.
 */
TILEDB_EXPORT int32_t tiledb_query_get_fragment_uri(
    tiledb_ctx_t* ctx,
    const tiledb_query_t* query,
    uint64_t idx,
    const char** uri);

/**
 * Retrieves the timestamp range of the written fragment with the input index.
 * Applicable only to WRITE queries.
 *
 * **Example:**
 *
 * @code{.c}
 * uint64_t t1, t2;
 * tiledb_query_get_fragment_timestamp_range(
 *     ctx, query, 0, &t1, &t2);
 * @endcode
 *
 * @param ctx The TileDB context
 * @param query The query.
 * @param idx The index of the written fragment.
 * @param t1 The start value of the timestamp range of the
 *     written fragment to be returned.
 * @param t2 The end value of the timestamp range of the
 *     written fragment to be returned.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_query_get_fragment_timestamp_range(
    tiledb_ctx_t* ctx,
    const tiledb_query_t* query,
    uint64_t idx,
    uint64_t* t1,
    uint64_t* t2);

/* ****************************** */
/*          QUERY CONDITION       */
/* ****************************** */

/**
 * Allocates a TileDB query condition object.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_query_condition_t* query_condition;
 * tiledb_query_condition_alloc(ctx, &query_condition);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param cond The allocated query condition object.
 * @return `TILEDB_OK` for success and `TILEDB_OOM` or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_query_condition_alloc(
    tiledb_ctx_t* ctx, tiledb_query_condition_t** cond);

/**
 * Frees a TileDB query condition object.
 *
 * **Example:**
 *
 * @code{.c}
 * uint32_t value = 5;
 * tiledb_query_condition_t* query_condition;
 * tiledb_query_condition_alloc(
 *   ctx, "longitude", &value, sizeof(value), TILEDB_LT, &query_condition);
 * tiledb_query_set_condition(ctx, query, query_condition);
 * tiledb_query_submit(ctx, query);
 * tiledb_query_condition_free(&query_condition);
 * @endcode
 *
 * @param cond The query condition object to be freed.
 */
TILEDB_EXPORT void tiledb_query_condition_free(tiledb_query_condition_t** cond);

/**
 * Initializes a TileDB query condition object.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_query_condition_t* query_condition;
 * tiledb_query_condition_alloc(ctx, &query_condition);
 *
 * uint32_t value = 5;
 * tiledb_query_condition_init(
 *   ctx, query_condition, "longitude", &value, sizeof(value), TILEDB_LT);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param cond The allocated query condition object.
 * @param attribute_name The attribute name.
 * @param condition_value The value to compare against an attribute value.
 * @param condition_value_size The byte size of `condition_value`.
 * @param op The comparison operator.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_query_condition_init(
    tiledb_ctx_t* ctx,
    tiledb_query_condition_t* cond,
    const char* attribute_name,
    const void* condition_value,
    uint64_t condition_value_size,
    tiledb_query_condition_op_t op);

/**
 * Combines two query condition objects into a newly allocated
 * condition. Does not mutate or free the input condition objects.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_query_condition_t* query_condition_1;
 * tiledb_query_condition_alloc(ctx, &query_condition_1);
 * uint32_t value_1 = 5;
 * tiledb_query_condition_init(
 *   ctx,
 *   query_condition_1,
 *   "longitude",
 *   &value_1,
 *   sizeof(value_1),
 *   TILEDB_LT);
 *
 * tiledb_query_condition_t* query_condition_2;
 * tiledb_query_condition_alloc(ctx, &query_condition_2);
 * uint32_t value_2 = 20;
 * tiledb_query_condition_init(
 *   ctx,
 *   query_condition_2,
 *   "latitude",
 *   &value_2,
 *   sizeof(value_2),
 *   TILEDB_GE);
 *
 * tiledb_query_condition_t* query_condition_3;
 * tiledb_query_condition_combine(
 *   ctx, query_condition_1, query_condition_2, TILEDB_AND, &query_condition_3);
 *
 * tiledb_query_condition_free(&query_condition_1);
 * tiledb_query_condition_free(&query_condition_2);
 *
 * tiledb_query_set_condition(ctx, query, query_condition_3);
 * tiledb_query_submit(ctx, query);
 * tiledb_query_condition_free(&query_condition_3);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param attribute_name The attribute name.
 * @param condition_value The value to compare against an attribute value.
 * @param condition_value_size The byte size of `condition_value`.
 * @param op The comparison operator.
 * @param cond The allocated query condition object.
 * @return `TILEDB_OK` for success and `TILEDB_OOM` or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_query_condition_combine(
    tiledb_ctx_t* ctx,
    const tiledb_query_condition_t* left_cond,
    const tiledb_query_condition_t* right_cond,
    tiledb_query_condition_combination_op_t combination_op,
    tiledb_query_condition_t** combined_cond);

/* ********************************* */
/*               ARRAY               */
/* ********************************* */

/**
 * Allocates a TileDB array object.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_array_t* array;
 * tiledb_array_alloc(ctx, "hdfs:///tiledb_arrays/my_array", &array);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array_uri The array URI.
 * @param array The array object to be created.
 * @return `TILEDB_OK` for success and `TILEDB_OOM` or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_alloc(
    tiledb_ctx_t* ctx, const char* array_uri, tiledb_array_t** array);

/**
 * Sets the starting timestamp to use when opening (and reopening) the array.
 * This is an inclusive bound. The default value is `0`.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_array_t* array;
 * tiledb_array_alloc(ctx, "s3://tiledb_bucket/my_array", &array);
 * tiledb_array_set_open_timestamp_start(ctx, array, 1234);
 * tiledb_array_open(ctx, array, TILEDB_READ);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array The array to set the timestamp on.
 * @param timestamp_start The epoch timestamp in milliseconds.
 * @return `TILEDB_OK` for success or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_set_open_timestamp_start(
    tiledb_ctx_t* ctx, tiledb_array_t* array, uint64_t timestamp_start);

/**
 * Sets the ending timestamp to use when opening (and reopening) the array.
 * This is an inclusive bound. The UINT64_MAX timestamp is a reserved timestamp
 * that will be interpretted as the current timestamp when an array is opened.
 * The default value is `UINT64_MAX`.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_array_t* array;
 * tiledb_array_alloc(ctx, "s3://tiledb_bucket/my_array", &array);
 * tiledb_array_set_open_timestamp_end(ctx, array, 5678);
 * tiledb_array_open(ctx, array, TILEDB_READ);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array The array to set the timestamp on.
 * @param timestamp_end The epoch timestamp in milliseconds. Use UINT64_MAX for
 *   the current timestamp.
 * @return `TILEDB_OK` for success or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_set_open_timestamp_end(
    tiledb_ctx_t* ctx, tiledb_array_t* array, uint64_t timestamp_end);

/**
 * Gets the starting timestamp used when opening (and reopening) the array.
 * This is an inclusive bound.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_array_t* array;
 * tiledb_array_alloc(ctx, "s3://tiledb_bucket/my_array", &array);
 * tiledb_array_set_open_timestamp_start(ctx, array, 1234);
 * tiledb_array_open(ctx, array, TILEDB_READ);
 *
 * uint64_t timestamp_start;
 * tiledb_array_get_open_timestamp_start(ctx, array, &timestamp_start);
 * assert(timestamp_start == 1234);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array The array to set the timestamp on.
 * @param timestamp_start The output epoch timestamp in milliseconds.
 * @return `TILEDB_OK` for success or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_get_open_timestamp_start(
    tiledb_ctx_t* ctx, tiledb_array_t* array, uint64_t* timestamp_start);

/**
 * Gets the ending timestamp used when opening (and reopening) the array.
 * This is an inclusive bound. If UINT64_MAX was set, this will return
 * the timestamp at the time the array was opened. If the array has not
 * yet been opened, it will return UINT64_MAX.`
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_array_t* array;
 * tiledb_array_alloc(ctx, "s3://tiledb_bucket/my_array", &array);
 * tiledb_array_set_open_timestamp_end(ctx, array, 5678);
 * tiledb_array_open(ctx, array, TILEDB_READ);
 *
 * uint64_t timestamp_end;
 * tiledb_array_get_open_timestamp_end(ctx, array, &timestamp_end);
 * assert(timestamp_start == 5678);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array The array to set the timestamp on.
 * @param timestamp_end The output epoch timestamp in milliseconds.
 * @return `TILEDB_OK` for success or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_get_open_timestamp_end(
    tiledb_ctx_t* ctx, tiledb_array_t* array, uint64_t* timestamp_end);

/**
 * Opens a TileDB array. The array is opened using a query type as input.
 * This is to indicate that queries created for this `tiledb_array_t`
 * object will inherit the query type. In other words, `tiledb_array_t`
 * objects are opened to receive only one type of queries.
 * They can always be closed and be re-opened with another query type.
 * Also there may be many different `tiledb_array_t`
 * objects created and opened with different query types.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_array_t* array;
 * tiledb_array_alloc(ctx, "hdfs:///tiledb_arrays/my_array", &array);
 * tiledb_array_open(ctx, array, TILEDB_READ);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array The array object to be opened.
 * @param query_type The type of queries the array object will be receiving.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 *
 * @note If the same array object is opened again without being closed,
 *     an error will be thrown.
 * @note The config should be set before opening an array.
 * @note If the array is to be opened at a specfic time interval, the
 *      `timestamp{start, end}` values should be set to a config that's set to
 *       the array object before opening the array.
 */
TILEDB_EXPORT int32_t tiledb_array_open(
    tiledb_ctx_t* ctx, tiledb_array_t* array, tiledb_query_type_t query_type);

/**
 * This is a deprecated API.
 *
 * Similar to `tiledb_array_open`, but this function takes as input a
 * timestamp, representing time in milliseconds ellapsed since
 * 1970-01-01 00:00:00 +0000 (UTC). Opening the array at a
 * timestamp provides a view of the array with all writes/updates that
 * happened at or before `timestamp` (i.e., excluding those that
 * occurred after `timestamp`). This function is useful to ensure
 * consistency at a potential distributed setting, where machines
 * need to operate on the same view of the array.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_array_t* array;
 * tiledb_array_alloc(ctx, "hdfs:///tiledb_arrays/my_array", &array);
 * // Assuming `timestamp` is time represented in milliseconds:
 * tiledb_array_open_at(ctx, array, TILEDB_READ, timestamp);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array The array object to be opened.
 * @param query_type The type of queries the array object will be receiving.
 * @param timestamp The timestamp to open the array at.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 *
 * @note If the same array object is opened again without being closed,
 *     an error will be thrown.
 * @note This function is applicable only to read queries.
 * @note The config should be set before opening an array.
 * @note If the array is to be opened at a specfic time interval, the
 *      `timestamp{start, end}` values should be set to a config that's set to
 *       the array object before opening the array.
 */
TILEDB_EXPORT int32_t tiledb_array_open_at(
    tiledb_ctx_t* ctx,
    tiledb_array_t* array,
    tiledb_query_type_t query_type,
    uint64_t timestamp);

/**
 * Opens an encrypted array using the given encryption key. This function has
 * the same semantics as `tiledb_array_open()` but is used for encrypted arrays.
 *
 * An encrypted array must be opened with this function before queries can be
 * issued to it.
 *
 * **Example:**
 *
 * @code{.c}
 * // Load AES-256 key from disk, environment variable, etc.
 * uint8_t key[32] = ...;
 * tiledb_array_t* array;
 * tiledb_array_alloc(ctx, "hdfs:///tiledb_arrays/my_array", &array);
 * tiledb_array_open_with_key(ctx, array, TILEDB_READ,
 *     TILEDB_AES_256_GCM, key, sizeof(key));
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array The array object to be opened.
 * @param query_type The type of queries the array object will be receiving.
 * @param encryption_type The encryption type to use.
 * @param encryption_key The encryption key to use.
 * @param key_length Length in bytes of the encryption key.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 *
 * @note The config should be set before opening an array.
 */
TILEDB_EXPORT int32_t tiledb_array_open_with_key(
    tiledb_ctx_t* ctx,
    tiledb_array_t* array,
    tiledb_query_type_t query_type,
    tiledb_encryption_type_t encryption_type,
    const void* encryption_key,
    uint32_t key_length);

/**
 * This is a deprecated API.
 *
 * Similar to `tiledb_array_open_with_key`, but this function takes as
 * input a timestamp, representing time in milliseconds ellapsed since
 * 1970-01-01 00:00:00 +0000 (UTC). Opening the array at a
 * timestamp provides a view of the array with all writes/updates that
 * happened at or before `timestamp` (i.e., excluding those that
 * occurred after `timestamp`). This function is useful to ensure
 * consistency at a potential distributed setting, where machines
 * need to operate on the same view of the array.
 *
 * **Example:**
 *
 * @code{.c}
 * // Load AES-256 key from disk, environment variable, etc.
 * uint8_t key[32] = ...;
 * tiledb_array_t* array;
 * tiledb_array_alloc(ctx, "hdfs:///tiledb_arrays/my_array", &array);
 * // Assuming `timestamp` is time represented in milliseconds:
 * tiledb_array_open_at_with_key(ctx, array, TILEDB_READ,
 *     TILEDB_AES_256_GCM, key, sizeof(key), timestamp);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array The array object to be opened.
 * @param query_type The type of queries the array object will be receiving.
 * @param encryption_type The encryption type to use.
 * @param encryption_key The encryption key to use.
 * @param key_length Length in bytes of the encryption key.
 * @param timestamp The timestamp to open the array at.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 *
 * @note If the same array object is opened again without being closed,
 *     an error will be thrown.
 * @note This function is applicable only to read queries.
 * @note The config should be set before opening an array.
 */
TILEDB_EXPORT int32_t tiledb_array_open_at_with_key(
    tiledb_ctx_t* ctx,
    tiledb_array_t* array,
    tiledb_query_type_t query_type,
    tiledb_encryption_type_t encryption_type,
    const void* encryption_key,
    uint32_t key_length,
    uint64_t timestamp);

/**
 * Checks if the array is open.
 *
 * @param ctx The TileDB context.
 * @param array The array to be checked.
 * @param is_open `1` if the array is open and `0` otherwise.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_is_open(
    tiledb_ctx_t* ctx, tiledb_array_t* array, int32_t* is_open);

/**
 * Reopens a TileDB array (the array must be already open). This is useful
 * when the array got updated after it got opened and the `tiledb_array_t`
 * object got created. To sync-up with the updates, the user must either
 * close the array and open with `tiledb_array_open`, or just use
 * `tiledb_array_reopen` without closing. This function will be generally
 * faster than the former alternative.
 *
 * Note: reopening encrypted arrays does not require the encryption key.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_array_t* array;
 * tiledb_array_alloc(ctx, "hdfs:///tiledb_arrays/my_array", &array);
 * tiledb_array_open(ctx, array, TILEDB_READ);
 * tiledb_array_reopen(ctx, array);
 *
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array The array object to be re-opened.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 *
 * @note This is applicable only to arrays opened for reads.
 * @note If the array is to be reopened after opening at a specfic time
 *      interval, the `timestamp{start, end}` values and subsequent config
 *      object should be reset for the array before reopening.
 */
TILEDB_EXPORT int32_t
tiledb_array_reopen(tiledb_ctx_t* ctx, tiledb_array_t* array);

/**
 * This is a deprecated API.
 *
 * Reopens a TileDB array (the array must be already open) at a specific
 * timestamp.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_array_t* array;
 * tiledb_array_alloc(ctx, "hdfs:///tiledb_arrays/my_array", &array);
 * tiledb_array_open(ctx, array, TILEDB_READ);
 * uint64_t timestamp = tiledb_timestamp_now_ms();
 * tiledb_array_reopen_at(ctx, array, timestamp);
 *
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array The array object to be re-opened.
 * @param timestamp Timestamp at which to reopen.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 *
 * @note This is applicable only to arrays opened for reads.
 * @note If the array is to be reopened after opening at a specfic time
 *      interval, the `timestamp{start, end}` values and subsequent config
 *      object should be reset for the array before reopening.
 */
TILEDB_EXPORT int32_t tiledb_array_reopen_at(
    tiledb_ctx_t* ctx, tiledb_array_t* array, uint64_t timestamp);

/**
 * This is a deprecated API. The start/end timestamps for opening an array
 * are now set in the config.
 *
 * Returns the timestamp, representing time in milliseconds ellapsed since
 * 1970-01-01 00:00:00 +0000 (UTC), at which the array was opened. See also the
 * documentation of `tiledb_array_open_at`.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_array_t* array;
 * tiledb_array_alloc(ctx, "s3://tiledb_bucket/my_array", &array);
 * tiledb_array_open(ctx, array, TILEDB_READ);
 * // Get the timestamp the array at which the array was opened.
 * uint64_t timestamp;
 * tiledb_array_get_timestamp(ctx, array, &timestamp);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array The array to retrieve the timestamp for.
 * @param timestamp Set to the timestamp at which the array was opened.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 *
 * @note The array does not need to be open to use this function.
 */
TILEDB_EXPORT int32_t tiledb_array_get_timestamp(
    tiledb_ctx_t* ctx, tiledb_array_t* array, uint64_t* timestamp);

/**
 * Sets the array config.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_array_t* array;
 * tiledb_array_alloc(ctx, "s3://tiledb_bucket/my_array", &array);
 * tiledb_array_open(ctx, array, TILEDB_READ);
 * // Set the config for the given array.
 * tiledb_config_t* config;
 * tiledb_array_set_config(ctx, array, config);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array The array to set the config for.
 * @param config The config to be set.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 *
 * @note The array does not need to be opened via `tiledb_array_open_at` to use
 *      this function.
 * @note The config should be set before opening an array.
 */
TILEDB_EXPORT int32_t tiledb_array_set_config(
    tiledb_ctx_t* ctx, tiledb_array_t* array, tiledb_config_t* config);

/**
 * Gets the array config.
 *
 * **Example:**
 *
 * @code{.c}
 * // Retrieve the config for the given array.
 * tiledb_config_t* config;
 * tiledb_array_get_config(ctx, array, config);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array The array to set the config for.
 * @param config Set to the retrieved config.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_get_config(
    tiledb_ctx_t* ctx, tiledb_array_t* array, tiledb_config_t** config);

/**
 * Closes a TileDB array.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_array_t* array;
 * tiledb_array_alloc(ctx, "hdfs:///tiledb_arrays/my_array", &array);
 * tiledb_array_open(ctx, array, TILEDB_READ);
 * tiledb_array_close(ctx, array);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array The array object to be closed.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 *
 * @note If the array object has already been closed, the function has
 *     no effect.
 */
TILEDB_EXPORT int32_t
tiledb_array_close(tiledb_ctx_t* ctx, tiledb_array_t* array);

/**
 * Frees a TileDB array object.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_array_t* array;
 * tiledb_array_alloc(ctx, "hdfs:///tiledb_arrays/my_array", &array);
 * tiledb_array_open(ctx, array, TILEDB_READ);
 * tiledb_array_close(ctx, array);
 * tiledb_array_free(&array);
 * @endcode
 *
 * @param array The array object to be freed.
 */
TILEDB_EXPORT void tiledb_array_free(tiledb_array_t** array);

/**
 * Retrieves the schema of an array.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_array_schema_t* array_schema;
 * tiledb_array_get_schema(ctx, array, &array_schema);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array The open array.
 * @param array_schema The array schema to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_OOM` or `TILEDB_ERR` for error.
 *
 * @note The user must free the array schema with `tiledb_array_schema_free`.
 */
TILEDB_EXPORT int32_t tiledb_array_get_schema(
    tiledb_ctx_t* ctx,
    tiledb_array_t* array,
    tiledb_array_schema_t** array_schema);

/**
 * Retrieves the query type with which the array was opened.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_array_t* array;
 * tiledb_array_alloc(ctx, "hdfs:///tiledb_arrays/my_array", &array);
 * tiledb_array_open(ctx, array, TILEDB_READ);
 * tiledb_query_type_t query_type;
 * tiledb_array_get_type(ctx, array, &query_type);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array The array.
 * @param query_type The query type to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_get_query_type(
    tiledb_ctx_t* ctx, tiledb_array_t* array, tiledb_query_type_t* query_type);

/**
 * Creates a new TileDB array given an input schema.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_array_create(ctx, "hdfs:///tiledb_arrays/my_array", array_schema);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array_uri The array name.
 * @param array_schema The array schema.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_create(
    tiledb_ctx_t* ctx,
    const char* array_uri,
    const tiledb_array_schema_t* array_schema);

/**
 * Creates a new encrypted TileDB array given an input schema.
 *
 * Encrypted arrays can only be created through this function.
 *
 * **Example:**
 *
 * @code{.c}
 * uint8_t key[32] = ...;
 * tiledb_array_create_with_key(
 *     ctx, "hdfs:///tiledb_arrays/my_array", array_schema,
 *     TILEDB_AES_256_GCM, key, sizeof(key));
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array_uri The array name.
 * @param array_schema The array schema.
 * @param encryption_type The encryption type to use.
 * @param encryption_key The encryption key to use.
 * @param key_length Length in bytes of the encryption key.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_create_with_key(
    tiledb_ctx_t* ctx,
    const char* array_uri,
    const tiledb_array_schema_t* array_schema,
    tiledb_encryption_type_t encryption_type,
    const void* encryption_key,
    uint32_t key_length);

/**
 * Depending on the consoliation mode in the config, consolidates either the
 * fragment files, fragment metadata files, or array metadata files into a
 * single file.
 *
 * You must first finalize all queries to the array before consolidation can
 * begin (as consolidation temporarily acquires an exclusive lock on the array).
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_array_consolidate(
 *     ctx, "hdfs:///tiledb_arrays/my_array", nullptr);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array_uri The name of the TileDB array whose metadata will
 *     be consolidated.
 * @param config Configuration parameters for the consolidation
 *     (`nullptr` means default, which will use the config from `ctx`).
 *     The `sm.consolidation.mode` parameter determines which type of
 *     consolidation to perform.
 *
 * @return `TILEDB_OK` on success, and `TILEDB_ERR` on error.
 */
TILEDB_EXPORT int32_t tiledb_array_consolidate(
    tiledb_ctx_t* ctx, const char* array_uri, tiledb_config_t* config);

/**
 * Depending on the consoliation mode in the config, consolidates either the
 * fragment files, fragment metadata files, or array metadata files into a
 * single file.
 *
 * You must first finalize all queries to the array before consolidation can
 * begin (as consolidation temporarily acquires an exclusive lock on the array).
 *
 * **Example:**
 *
 * @code{.c}
 * uint8_t key[32] = ...;
 * tiledb_array_consolidate_with_key(
 *     ctx, "hdfs:///tiledb_arrays/my_array",
 *     TILEDB_AES_256_GCM, key, sizeof(key), nullptr);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array_uri The name of the TileDB array to be consolidated.
 * @param encryption_type The encryption type to use.
 * @param encryption_key The encryption key to use.
 * @param key_length Length in bytes of the encryption key.
 * @param config Configuration parameters for the consolidation
 *     (`nullptr` means default, which will use the config from `ctx`).
 *     The `sm.consolidation.mode` parameter determines which type of
 *     consolidation to perform.
 *
 * @return `TILEDB_OK` on success, and `TILEDB_ERR` on error.
 */
TILEDB_EXPORT int32_t tiledb_array_consolidate_with_key(
    tiledb_ctx_t* ctx,
    const char* array_uri,
    tiledb_encryption_type_t encryption_type,
    const void* encryption_key,
    uint32_t key_length,
    tiledb_config_t* config);

/**
 * Cleans up the array, such as consolidated fragments and array metadata.
 * Note that this will coarsen the granularity of time traveling (see docs
 * for more information).
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_array_vacuum(ctx, "hdfs:///tiledb_arrays/my_array");
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array_uri The name of the TileDB array to vacuum.
 * @param config Configuration parameters for the vacuuming
 *     (`nullptr` means default, which will use the config from `ctx`).
 * @return `TILEDB_OK` on success, and `TILEDB_ERR` on error.
 */
TILEDB_EXPORT int32_t tiledb_array_vacuum(
    tiledb_ctx_t* ctx, const char* array_uri, tiledb_config_t* config);

/**
 * Retrieves the non-empty domain from an array. This is the union of the
 * non-empty domains of the array fragments.
 *
 * **Example:**
 *
 * @code{.c}
 * uint64_t domain[4]; // Assuming a 2D array, 2 [low, high] pairs
 * int32_t is_empty;
 * tiledb_array_t* array;
 * tiledb_array_alloc(ctx, "my_array", &array);
 * tiledb_array_open(ctx, array, TILEDB_READ);
 * tiledb_array_get_non_empty_domain(ctx, array, domain, &is_empty);
 * @endcode
 *
 * @param ctx The TileDB context
 * @param array The array object (must be opened beforehand).
 * @param domain The domain to be retrieved.
 * @param is_empty The function sets it to `1` if the non-empty domain is
 *     empty (i.e., the array does not contain any data yet), and `0` otherwise.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_get_non_empty_domain(
    tiledb_ctx_t* ctx, tiledb_array_t* array, void* domain, int32_t* is_empty);

/**
 * Retrieves the non-empty domain from an array for a given dimension index.
 * This is the union of the non-empty domains of the array fragments on
 * the given dimension.
 *
 * **Example:**
 *
 * @code{.c}
 * uint64_t domain[2];
 * int32_t is_empty;
 * tiledb_array_t* array;
 * tiledb_array_alloc(ctx, "my_array", &array);
 * tiledb_array_open(ctx, array, TILEDB_READ);
 * tiledb_array_get_non_empty_domain_from_index(ctx, array, 0, domain,
 * &is_empty);
 * @endcode
 *
 * @param ctx The TileDB context
 * @param array The array object (must be opened beforehand).
 * @param idx The dimension index, following the order as it was defined
 *      in the domain of the array schema.
 * @param domain The domain to be retrieved.
 * @param is_empty The function sets it to `1` if the non-empty domain is
 *     empty (i.e., the array does not contain any data yet), and `0` otherwise.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_get_non_empty_domain_from_index(
    tiledb_ctx_t* ctx,
    tiledb_array_t* array,
    uint32_t idx,
    void* domain,
    int32_t* is_empty);

/**
 * Retrieves the non-empty domain from an array for a given dimension name.
 * This is the union of the non-empty domains of the array fragments on
 * the given dimension.
 *
 * **Example:**
 *
 * @code{.c}
 * uint64_t domain[2];
 * int32_t is_empty;
 * tiledb_array_t* array;
 * tiledb_array_alloc(ctx, "my_array", &array);
 * tiledb_array_open(ctx, array, TILEDB_READ);
 * tiledb_array_get_non_empty_domain_from_name(ctx, array, "d1", domain,
 * &is_empty);
 * @endcode
 *
 * @param ctx The TileDB context
 * @param array The array object (must be opened beforehand).
 * @param name The dimension name.
 * @param domain The domain to be retrieved.
 * @param is_empty The function sets it to `1` if the non-empty domain is
 *     empty (i.e., the array does not contain any data yet), and `0` otherwise.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_get_non_empty_domain_from_name(
    tiledb_ctx_t* ctx,
    tiledb_array_t* array,
    const char* name,
    void* domain,
    int32_t* is_empty);

/**
 * Retrieves the non-empty domain range sizes from an array for a given
 * dimension index. This is the union of the non-empty domains of the array
 * fragments on the given dimension. Applicable only to var-sized dimensions.
 *
 * **Example:**
 *
 * @code{.c}
 * int32_t is_empty;
 * tiledb_array_t* array;
 * tiledb_array_alloc(ctx, "my_array", &array);
 * tiledb_array_open(ctx, array, TILEDB_READ);
 * uint64_t start_size, end_size;
 * tiledb_array_get_non_empty_domain_var_size_from_index(
 *     ctx, array, 0, &start_size, &end_size, &is_empty);
 * // If non-empty domain range is `[aa, dddd]`, then `start_size = 2`
 * // and `end_size = 4`.
 * @endcode
 *
 * @param ctx The TileDB context
 * @param array The array object (must be opened beforehand).
 * @param idx The dimension index, following the order as it was defined
 *      in the domain of the array schema.
 * @param start_size The size in bytes of the start range.
 * @param end_size The size in bytes of the end range.
 * @param is_empty The function sets it to `1` if the non-empty domain is
 *     empty (i.e., the array does not contain any data yet), and `0` otherwise.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_get_non_empty_domain_var_size_from_index(
    tiledb_ctx_t* ctx,
    tiledb_array_t* array,
    uint32_t idx,
    uint64_t* start_size,
    uint64_t* end_size,
    int32_t* is_empty);

/**
 * Retrieves the non-empty domain range sizes from an array for a given
 * dimension name. This is the union of the non-empty domains of the array
 * fragments on the given dimension. Applicable only to var-sized dimensions.
 *
 * **Example:**
 *
 * @code{.c}
 * int32_t is_empty;
 * tiledb_array_t* array;
 * tiledb_array_alloc(ctx, "my_array", &array);
 * tiledb_array_open(ctx, array, TILEDB_READ);
 * uint64_t start_size, end_size;
 * tiledb_array_get_non_empty_domain_var_size_from_name(
 *     ctx, array, "d", &start_size, &end_size, &is_empty);
 * // If non-empty domain range is `[aa, dddd]`, then `start_size = 2`
 * // and `end_size = 4`.
 * @endcode
 *
 * @param ctx The TileDB context
 * @param array The array object (must be opened beforehand).
 * @param name The dimension name.
 * @param start_size The size in bytes of the start range.
 * @param end_size The size in bytes of the end range.
 * @param is_empty The function sets it to `1` if the non-empty domain is
 *     empty (i.e., the array does not contain any data yet), and `0` otherwise.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_get_non_empty_domain_var_size_from_name(
    tiledb_ctx_t* ctx,
    tiledb_array_t* array,
    const char* name,
    uint64_t* start_size,
    uint64_t* end_size,
    int32_t* is_empty);

/**
 * Retrieves the non-empty domain from an array for a given
 * dimension index. This is the union of the non-empty domains of the array
 * fragments on the given dimension. Applicable only to var-sized dimensions.
 *
 * **Example:**
 *
 * @code{.c}
 * int32_t is_empty;
 * tiledb_array_t* array;
 * tiledb_array_alloc(ctx, "my_array", &array);
 * tiledb_array_open(ctx, array, TILEDB_READ);
 *
 * // Get range sizes first
 * uint64_t start_size, end_size;
 * tiledb_array_get_non_empty_domain_var_size_from_index(
 *     ctx, array, 0, &start_size, &end_size, &is_empty);
 *
 * // Get domain
 * char start[start_size];
 * char end[end_size];
 * tiledb_array_get_non_empty_domain_var_from_index(
 *     ctx, array, 0, start, end, &is_empty);
 * @endcode
 *
 * @param ctx The TileDB context
 * @param array The array object (must be opened beforehand).
 * @param idx The dimension index, following the order as it was defined
 *      in the domain of the array schema.
 * @param start The domain range start to set.
 * @param end The domain range end to set.
 * @param is_empty The function sets it to `1` if the non-empty domain is
 *     empty (i.e., the array does not contain any data yet), and `0` otherwise.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_get_non_empty_domain_var_from_index(
    tiledb_ctx_t* ctx,
    tiledb_array_t* array,
    uint32_t idx,
    void* start,
    void* end,
    int32_t* is_empty);

/**
 * Retrieves the non-empty domain from an array for a given
 * dimension name. This is the union of the non-empty domains of the array
 * fragments on the given dimension. Applicable only to var-sized dimensions.
 *
 * **Example:**
 *
 * @code{.c}
 * uint64_t domain[2];
 * int32_t is_empty;
 * tiledb_array_t* array;
 * tiledb_array_alloc(ctx, "my_array", &array);
 * tiledb_array_open(ctx, array, TILEDB_READ);
 *
 * // Get range sizes first
 * uint64_t start_size, end_size;
 * tiledb_array_get_non_empty_domain_var_size_from_name(
 *     ctx, array, "d", &start_size, &end_size, &is_empty);
 *
 * // Get domain
 * char start[start_size];
 * char end[end_size];
 * tiledb_array_get_non_empty_domain_var_from_name(
 *     ctx, array, "d", start, end, &is_empty);
 * @endcode
 *
 * @param ctx The TileDB context
 * @param array The array object (must be opened beforehand).
 * @param name The dimension name.
 * @param start The domain range start to set.
 * @param end The domain range end to set.
 * @param is_empty The function sets it to `1` if the non-empty domain is
 *     empty (i.e., the array does not contain any data yet), and `0` otherwise.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_get_non_empty_domain_var_from_name(
    tiledb_ctx_t* ctx,
    tiledb_array_t* array,
    const char* name,
    void* start,
    void* end,
    int32_t* is_empty);

/**
 * Computes an upper bound on the buffer size (in bytes) required for a read
 * query, for a given **fixed-sized* attribute/dimension and subarray.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_array_t* array;
 * tiledb_array_alloc(ctx, "my_array", &array);
 * tiledb_array_open(ctx, array, TILEDB_READ);
 * uint64_t buffer_size;
 * const char* attribute = "attr_1";
 * uint64_t subarray[] = {10, 20, 10, 100};
 * tiledb_array_max_buffer_size(ctx, array, attribute, subarray, buffer_size);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array The array object (must be opened beforehand).
 * @param name The fixed-sized attribute/dimension to focus on.
 * @param subarray The subarray to focus on. Note that it must have the same
 *     underlying type as the array domain.
 * @param buffer_size The buffer size (in bytes) to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_DEPRECATED_EXPORT int32_t tiledb_array_max_buffer_size(
    tiledb_ctx_t* ctx,
    tiledb_array_t* array,
    const char* name,
    const void* subarray,
    uint64_t* buffer_size);

/**
 * Computes an upper bound on the buffer size (in bytes) required for a read
 * query, for a given **var-sized* attribute/dimension and subarray.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_array_t* array;
 * tiledb_array_alloc(ctx, "my_array", &array);
 * tiledb_array_open(ctx, array, TILEDB_READ);
 * uint64_t buffer_off_size, buffer_val_size;
 * const char* attribute = "attr_2";
 * uint64_t subarray[] = {10, 20, 10, 100};
 * tiledb_array_max_buffer_size_var(
 *     ctx, array, attribute, subarray, buffer_off_size, buffer_val_size);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array The array object (must be opened beforehand).
 * @param name The var-sized attribute/dimension to focus on.
 * @param subarray The subarray to focus on. Note that it must have the same
 *     underlying type as the array domain.
 * @param buffer_off_size The offsets buffer size (in bytes) to be retrieved.
 * @param buffer_val_size The values buffer size (in bytes) to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_DEPRECATED_EXPORT int32_t tiledb_array_max_buffer_size_var(
    tiledb_ctx_t* ctx,
    tiledb_array_t* array,
    const char* name,
    const void* subarray,
    uint64_t* buffer_off_size,
    uint64_t* buffer_val_size);

/**
 * Retrieves the URI the array was opened with. It outputs an error
 * if the array is not open.
 *
 * @param ctx The TileDB context.
 * @param array The input array.
 * @param array_uri The array URI to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_get_uri(
    tiledb_ctx_t* ctx, tiledb_array_t* array, const char** array_uri);

/**
 * Retrieves the encryption type the array at the given URI was created with.
 *
 * @param ctx The TileDB context.
 * @param array_uri The array URI.
 * @param encryption_type The array encryption type to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_encryption_type(
    tiledb_ctx_t* ctx,
    const char* array_uri,
    tiledb_encryption_type_t* encryption_type);

/**
 * It puts a metadata key-value item to an open array. The array must
 * be opened in WRITE mode, otherwise the function will error out.
 *
 * @param ctx The TileDB context.
 * @param array An array opened in WRITE mode.
 * @param key The key of the metadata item to be added. UTF-8 encodings
 *     are acceptable.
 * @param value_type The datatype of the value.
 * @param value_num The value may consist of more than one items of the
 *     same datatype. This argument indicates the number of items in the
 *     value component of the metadata.
 * @param value The metadata value in binary form.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 *
 * @note The writes will take effect only upon closing the array.
 */
TILEDB_EXPORT int32_t tiledb_array_put_metadata(
    tiledb_ctx_t* ctx,
    tiledb_array_t* array,
    const char* key,
    tiledb_datatype_t value_type,
    uint32_t value_num,
    const void* value);

/**
 * It deletes a metadata key-value item from an open array. The array must
 * be opened in WRITE mode, otherwise the function will error out.
 *
 * @param ctx The TileDB context.
 * @param array An array opened in WRITE mode.
 * @param key The key of the metadata item to be deleted.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 *
 * @note The writes will take effect only upon closing the array.
 *
 * @note If the key does not exist, this will take no effect
 *     (i.e., the function will not error out).
 */
TILEDB_EXPORT int32_t tiledb_array_delete_metadata(
    tiledb_ctx_t* ctx, tiledb_array_t* array, const char* key);

/**
 * It gets a metadata key-value item from an open array. The array must
 * be opened in READ mode, otherwise the function will error out.
 *
 * @param ctx The TileDB context.
 * @param array An array opened in READ mode.
 * @param key The key of the metadata item to be retrieved. UTF-8 encodings
 *     are acceptable.
 * @param value_type The datatype of the value.
 * @param value_num The value may consist of more than one items of the
 *     same datatype. This argument indicates the number of items in the
 *     value component of the metadata. Keys with empty values are indicated
 *     by value_num == 1 and value == NULL.
 * @param value The metadata value in binary form.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 *
 * @note If the key does not exist, then `value` will be NULL.
 */
TILEDB_EXPORT int32_t tiledb_array_get_metadata(
    tiledb_ctx_t* ctx,
    tiledb_array_t* array,
    const char* key,
    tiledb_datatype_t* value_type,
    uint32_t* value_num,
    const void** value);

/**
 * It gets then number of metadata items in an open array. The array must
 * be opened in READ mode, otherwise the function will error out.
 *
 * @param ctx The TileDB context.
 * @param array An array opened in READ mode.
 * @param num The number of metadata items to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_get_metadata_num(
    tiledb_ctx_t* ctx, tiledb_array_t* array, uint64_t* num);

/**
 * It gets a metadata item from an open array using an index.
 * The array must be opened in READ mode, otherwise the function will
 * error out.
 *
 * @param ctx The TileDB context.
 * @param array An array opened in READ mode.
 * @param index The index used to get the metadata.
 * @param key The metadata key.
 * @param key_len The metadata key length.
 * @param value_type The datatype of the value.
 * @param value_num The value may consist of more than one items of the
 *     same datatype. This argument indicates the number of items in the
 *     value component of the metadata.
 * @param value The metadata value in binary form.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_array_get_metadata_from_index(
    tiledb_ctx_t* ctx,
    tiledb_array_t* array,
    uint64_t index,
    const char** key,
    uint32_t* key_len,
    tiledb_datatype_t* value_type,
    uint32_t* value_num,
    const void** value);

/**
 * Checks whether a key exists in metadata from an open array. The array must
 * be opened in READ mode, otherwise the function will error out.
 *
 * @param ctx The TileDB context.
 * @param array An array opened in READ mode.
 * @param key The key to be checked. UTF-8 encoding are acceptable.
 * @param value_type The datatype of the value, if any.
 * @param has_key Set to `1` if the metadata with given key exists, else `0`.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 *
 * @note If the key does not exist, then `value` will be NULL.
 */
TILEDB_EXPORT int32_t tiledb_array_has_metadata_key(
    tiledb_ctx_t* ctx,
    tiledb_array_t* array,
    const char* key,
    tiledb_datatype_t* value_type,
    int32_t* has_key);

/**
 * Consolidates the array metadata into a single array metadata file.
 *
 * You must first finalize all queries to the array before consolidation can
 * begin (as consolidation temporarily acquires an exclusive lock on the array).
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_array_consolidate_metadata(
 *     ctx, "hdfs:///tiledb_arrays/my_array", nullptr);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array_uri The name of the TileDB array whose metadata will
 *     be consolidated.
 * @param config Configuration parameters for the consolidation
 *     (`nullptr` means default, which will use the config from `ctx`).
 * @return `TILEDB_OK` on success, and `TILEDB_ERR` on error.
 */
TILEDB_DEPRECATED_EXPORT int32_t tiledb_array_consolidate_metadata(
    tiledb_ctx_t* ctx, const char* array_uri, tiledb_config_t* config);

/**
 * Consolidates the array metadata of an encrypted array into a single file.
 *
 * You must first finalize all queries to the array before consolidation can
 * begin (as consolidation temporarily acquires an exclusive lock on the array).
 *
 * **Example:**
 *
 * @code{.c}
 * uint8_t key[32] = ...;
 * tiledb_array_consolidate_metadata_with_key(
 *     ctx, "hdfs:///tiledb_arrays/my_array",
 *     TILEDB_AES_256_GCM, key, sizeof(key), nullptr);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array_uri The name of the TileDB array to be consolidated.
 * @param encryption_type The encryption type to use.
 * @param encryption_key The encryption key to use.
 * @param key_length Length in bytes of the encryption key.
 * @param config Configuration parameters for the consolidation
 *     (`nullptr` means default, which will use the config from `ctx`).
 *
 * @return `TILEDB_OK` on success, and `TILEDB_ERR` on error.
 */
TILEDB_DEPRECATED_EXPORT int32_t tiledb_array_consolidate_metadata_with_key(
    tiledb_ctx_t* ctx,
    const char* array_uri,
    tiledb_encryption_type_t encryption_type,
    const void* encryption_key,
    uint32_t key_length,
    tiledb_config_t* config);

/* ********************************* */
/*          OBJECT MANAGEMENT        */
/* ********************************* */

/**
 * Returns the TileDB object type for a given resource path.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_object_t type;
 * tiledb_object_type(ctx, "arrays/my_array", &type);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param path The URI path to the TileDB resource.
 * @param type The type to be retrieved.
 * @return `TILEDB_OK` on success, `TILEDB_ERR` on error.
 */
TILEDB_EXPORT int32_t
tiledb_object_type(tiledb_ctx_t* ctx, const char* path, tiledb_object_t* type);

/**
 * Deletes a TileDB resource (group, array, key-value).
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_object_remove(ctx, "arrays/my_array");
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param path The URI path to the tiledb resource.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_object_remove(tiledb_ctx_t* ctx, const char* path);

/**
 * Moves a TileDB resource (group, array, key-value).
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_object_move(ctx, "arrays/my_array", "arrays/my_array_2");
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param old_path The old TileDB directory.
 * @param new_path The new TileDB directory.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_object_move(
    tiledb_ctx_t* ctx, const char* old_path, const char* new_path);

/**
 * Walks (iterates) over the TileDB objects contained in *path*. The traversal
 * is done recursively in the order defined by the user. The user provides
 * a callback function which is applied on each of the visited TileDB objects.
 * The iteration continues for as long the callback returns non-zero, and stops
 * when the callback returns 0. Note that this function ignores any object
 * (e.g., file or directory) that is not TileDB-related.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_object_walk(ctx, "arrays", TILEDB_PREORDER, NULL, NULL);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param path The path in which the traversal will occur.
 * @param order The order of the recursive traversal (e.g., pre-order or
 *     post-order.
 * @param callback The callback function to be applied on every visited object.
 *     The callback should return `0` if the iteration must stop, and `1`
 *     if the iteration must continue. It takes as input the currently visited
 *     path, the type of that path (e.g., array or group), and the data
 *     provided by the user for the callback. The callback returns `-1` upon
 *     error. Note that `path` in the callback will be an **absolute** path.
 * @param data The data passed in the callback as the last argument.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_object_walk(
    tiledb_ctx_t* ctx,
    const char* path,
    tiledb_walk_order_t order,
    int32_t (*callback)(const char*, tiledb_object_t, void*),
    void* data);

/**
 * Similar to `tiledb_walk`, but now the function visits only the children of
 * `path` (i.e., it does not recursively continue to the children directories).
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_object_ls(ctx, "arrays", NULL, NULL);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param path The path in which the traversal will occur.
 * @param callback The callback function to be applied on every visited object.
 *     The callback should return `0` if the iteration must stop, and `1`
 *     if the iteration must continue. It takes as input the currently visited
 *     path, the type of that path (e.g., array or group), and the data
 *     provided by the user for the callback. The callback returns `-1` upon
 *     error. Note that `path` in the callback will be an **absolute** path.
 * @param data The data passed in the callback as the last argument.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_object_ls(
    tiledb_ctx_t* ctx,
    const char* path,
    int32_t (*callback)(const char*, tiledb_object_t, void*),
    void* data);

/* ****************************** */
/*        VIRTUAL FILESYSTEM      */
/* ****************************** */

/**
 * Creates a virtual filesystem object.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_vfs_t* vfs;
 * tiledb_vfs_alloc(ctx, config, &vfs);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param vfs The virtual filesystem object to be created.
 * @param config Configuration parameters.
 * @return `TILEDB_OK` for success and `TILEDB_OOM` or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_vfs_alloc(
    tiledb_ctx_t* ctx, tiledb_config_t* config, tiledb_vfs_t** vfs);

/**
 * Frees a virtual filesystem object.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_vfs_free(&vfs);
 * @endcode
 *
 * @param vfs The virtual filesystem object to be freed.
 */
TILEDB_EXPORT void tiledb_vfs_free(tiledb_vfs_t** vfs);

/**
 * Retrieves the config from a VFS context.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_config_t* config;
 * tiledb_vfs_get_config(ctx, vfs, &config);
 * // Make sure to free the retrieved config
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param vfs The VFS object.
 * @param config The config to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_OOM` or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_vfs_get_config(
    tiledb_ctx_t* ctx, tiledb_vfs_t* vfs, tiledb_config_t** config);

/**
 * Creates an object-store bucket.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_vfs_create_bucket(ctx, vfs, "s3://tiledb");
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param vfs The virtual filesystem object.
 * @param uri The URI of the bucket to be created.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_vfs_create_bucket(tiledb_ctx_t* ctx, tiledb_vfs_t* vfs, const char* uri);

/**
 * Deletes an object-store bucket.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_vfs_delete_bucket(ctx, vfs, "s3://tiledb");
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param vfs The virtual filesystem object.
 * @param uri The URI of the bucket to be deleted.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_vfs_remove_bucket(tiledb_ctx_t* ctx, tiledb_vfs_t* vfs, const char* uri);

/**
 * Deletes the contents of an object-store bucket.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_vfs_empty_bucket(ctx, vfs, "s3://tiledb");
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param vfs The virtual filesystem object.
 * @param uri The URI of the bucket to be emptied.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_vfs_empty_bucket(tiledb_ctx_t* ctx, tiledb_vfs_t* vfs, const char* uri);

/**
 * Checks if an object-store bucket is empty.
 *
 * **Example:**
 *
 * @code{.c}
 * int32_t is_empty;
 * tiledb_vfs_is_empty_bucket(ctx, vfs, "s3://tiledb", &empty);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param vfs The virtual filesystem object.
 * @param uri The URI of the bucket.
 * @param is_empty Sets it to `1` if the input bucket is empty,
 *     and `0` otherwise.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_vfs_is_empty_bucket(
    tiledb_ctx_t* ctx, tiledb_vfs_t* vfs, const char* uri, int32_t* is_empty);

/**
 * Checks if an object-store bucket exists.
 *
 * **Example:**
 *
 * @code{.c}
 * int32_t exists;
 * tiledb_vfs_is_bucket(ctx, vfs, "s3://tiledb", &exists);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param vfs The virtual filesystem object.
 * @param uri The URI of the bucket.
 * @param is_bucket Sets it to `1` if the input URI is a bucket, and `0`
 *     otherwise.
 * @return TILEDB_OK for success and TILEDB_ERR for error.
 */
TILEDB_EXPORT int32_t tiledb_vfs_is_bucket(
    tiledb_ctx_t* ctx, tiledb_vfs_t* vfs, const char* uri, int32_t* is_bucket);

/**
 * Creates a directory.
 *
 * - On S3, this is a noop.
 * - On all other backends, if the directory exists, the function
 *   just succeeds without doing anything.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_vfs_create_dir(ctx, vfs, "hdfs:///temp/my_dir");
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param vfs The virtual filesystem object.
 * @param uri The URI of the directory to be created.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_vfs_create_dir(tiledb_ctx_t* ctx, tiledb_vfs_t* vfs, const char* uri);

/**
 * Checks if a directory exists.
 *
 * **Example:**
 *
 * @code{.c}
 * int32_t exists;
 * tiledb_vfs_is_dir(ctx, vfs, "hdfs:///temp/my_dir", &exists);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param vfs The virtual filesystem object.
 * @param uri The URI of the directory.
 * @param is_dir Sets it to `1` if the directory exists and `0`
 *     otherwise.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 *
 * @note For S3, this function will return `true` if there is an object
 *     with prefix `uri/` (TileDB will append `/` internally to `uri`
 *     only if it does not exist), and `false` othewise.
 */
TILEDB_EXPORT int32_t tiledb_vfs_is_dir(
    tiledb_ctx_t* ctx, tiledb_vfs_t* vfs, const char* uri, int32_t* is_dir);

/**
 * Removes a directory (recursively).
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_vfs_remove_dir(ctx, vfs, "hdfs:///temp/my_dir");
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param vfs The virtual filesystem object.
 * @param uri The uri of the directory to be removed
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_vfs_remove_dir(tiledb_ctx_t* ctx, tiledb_vfs_t* vfs, const char* uri);

/**
 * Checks if a file exists.
 *
 * **Example:**
 *
 * @code{.c}
 * int32_t exists;
 * tiledb_vfs_is_file(ctx, vfs, "hdfs:///temp/my_file", &is_file);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param vfs The virtual filesystem object.
 * @param uri The URI of the file.
 * @param is_file Sets it to `1` if the file exists and `0` otherwise.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_vfs_is_file(
    tiledb_ctx_t* ctx, tiledb_vfs_t* vfs, const char* uri, int32_t* is_file);

/**
 * Deletes a file.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_vfs_remove_file(ctx, vfs, "hdfs:///temp/my_file");
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param vfs The virtual filesystem object.
 * @param uri The URI of the file.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_vfs_remove_file(tiledb_ctx_t* ctx, tiledb_vfs_t* vfs, const char* uri);

/**
 * Retrieves the size of a directory. This function is **recursive**, i.e.,
 * it will consider all files in the directory tree rooted at `uri`.
 *
 * **Example:**
 *
 * @code{.c}
 * uint64_t dir_size;
 * tiledb_vfs_dir_size(ctx, vfs, "hdfs:///temp/my_dir", &dir_size);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param vfs The virtual filesystem object.
 * @param uri The URI of the directory.
 * @param size The directory size to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_vfs_dir_size(
    tiledb_ctx_t* ctx, tiledb_vfs_t* vfs, const char* uri, uint64_t* size);

/**
 * Retrieves the size of a file.
 *
 * **Example:**
 *
 * @code{.c}
 * uint64_t file_size;
 * tiledb_vfs_file_size(ctx, vfs, "hdfs:///temp/my_file", &file_size);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param vfs The virtual filesystem object.
 * @param uri The URI of the file.
 * @param size The file size to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_vfs_file_size(
    tiledb_ctx_t* ctx, tiledb_vfs_t* vfs, const char* uri, uint64_t* size);

/**
 * Renames a file. If the destination file exists, it will be overwritten.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_vfs_move_file(
 * ctx, vfs, "hdfs:///temp/my_file", "hdfs::///new_file");
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param vfs The virtual filesystem object.
 * @param old_uri The old URI.
 * @param new_uri The new URI.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_vfs_move_file(
    tiledb_ctx_t* ctx,
    tiledb_vfs_t* vfs,
    const char* old_uri,
    const char* new_uri);

/**
 * Renames a directory.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_vfs_move_dir(ctx, vfs, "hdfs:///temp/my_dir", "hdfs::///new_dir");
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param vfs The virtual filesystem object.
 * @param old_uri The old URI.
 * @param new_uri The new URI.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_vfs_move_dir(
    tiledb_ctx_t* ctx,
    tiledb_vfs_t* vfs,
    const char* old_uri,
    const char* new_uri);

/**
 * Copies a file. If the destination file exists, it will be overwritten.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_vfs_copy_file(
 * ctx, vfs, "hdfs:///temp/my_file", "hdfs::///new_file");
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param vfs The virtual filesystem object.
 * @param old_uri The old URI.
 * @param new_uri The new URI.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_vfs_copy_file(
    tiledb_ctx_t* ctx,
    tiledb_vfs_t* vfs,
    const char* old_uri,
    const char* new_uri);

/**
 * Copies a directory. If the destination directory exists, it will be
 * overwritten.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_vfs_copy_dir(
 *  ctx, vfs, "hdfs:///temp/my_dir", "hdfs::///new_dir");
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param vfs The virtual filesystem object.
 * @param old_uri The old URI.
 * @param new_uri The new URI.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_vfs_copy_dir(
    tiledb_ctx_t* ctx,
    tiledb_vfs_t* vfs,
    const char* old_uri,
    const char* new_uri);

/**
 * Prepares a file for reading/writing.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_vfs_fh_t* fh;
 * tiledb_vfs_open(ctx, vfs, "some_file", TILEDB_VFS_READ, &fh);
 * // Make sure to close and delete the created file handle
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param vfs The virtual filesystem object.
 * @param uri The URI of the file.
 * @param mode The mode in which the file is opened:
 *     - `TILEDB_VFS_READ`:
 *       The file is opened for reading. An error is returned if the file
 *       does not exist.
 *     - `TILEDB_VFS_WRITE`:
 *       The file is opened for writing. If the file exists, it will be
 *       overwritten.
 *     - `TILEDB_VFS_APPEND`:
 *       The file is opened for writing. If the file exists, the write
 *       will start from the end of the file. Note that S3 does not
 *       support this operation and, thus, an error will be thrown in
 *       that case.
 * @param fh The file handle that is created. This will be used in
 *     `tiledb_vfs_read`, `tiledb_vfs_write` and `tiledb_vfs_sync`.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` or `TILEDB_OOM` for error.
 *
 * @note If the file is closed after being opened, without having
 *     written any data to it, the file will not be created. If you
 *     wish to create an empty file, use `tiledb_vfs_touch`
 *     instead.
 */
TILEDB_EXPORT int32_t tiledb_vfs_open(
    tiledb_ctx_t* ctx,
    tiledb_vfs_t* vfs,
    const char* uri,
    tiledb_vfs_mode_t mode,
    tiledb_vfs_fh_t** fh);

/**
 * Closes a file. This is flushes the buffered data into the file
 * when the file was opened in write (or append) mode. It is particularly
 * important to be called after S3 writes, as otherwise the writes will
 * not take effect.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_vfs_close(ctx, vfs, fh);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param fh The file handle.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_vfs_close(tiledb_ctx_t* ctx, tiledb_vfs_fh_t* fh);

/**
 * Reads from a file.
 *
 * **Example:**
 *
 * @code{.c}
 * char buffer[10000];
 * tiledb_vfs_read(ctx, fh, 100, buffer, 10000);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param fh The URI file handle.
 * @param offset The offset in the file where the read begins.
 * @param buffer The buffer to read into.
 * @param nbytes Number of bytes to read.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_vfs_read(
    tiledb_ctx_t* ctx,
    tiledb_vfs_fh_t* fh,
    uint64_t offset,
    void* buffer,
    uint64_t nbytes);

/**
 * Writes the contents of a buffer into a file. Note that this
 * function only **appends** data at the end of the file. If the
 * file does not exist, it will be created.
 *
 * **Example:**
 *
 * @code{.c}
 * const char* msg = "This will be written to the file";
 * tiledb_vfs_write(ctx, fh, buffer, strlen(msg));
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param fh The URI file handle.
 * @param buffer The buffer to write from.
 * @param nbytes Number of bytes to write.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_vfs_write(
    tiledb_ctx_t* ctx,
    tiledb_vfs_fh_t* fh,
    const void* buffer,
    uint64_t nbytes);

/**
 * Syncs (flushes) a file.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_vfs_sync(ctx, fh);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param fh The URI file handle.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 *
 * @note This has no effect for S3.
 */
TILEDB_EXPORT int32_t tiledb_vfs_sync(tiledb_ctx_t* ctx, tiledb_vfs_fh_t* fh);

/**
 * The function visits only the children of `path` (i.e., it does not
 * recursively continue to the children directories) and applies the `callback`
 * function using the input `data`.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_vfs_ls(ctx, vfs, "my_dir", NULL, NULL);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param vfs The virtual filesystem object.
 * @param path The path in which the traversal will occur.
 * @param callback The callback function to be applied on every visited object.
 *     The callback should return `0` if the iteration must stop, and `1`
 *     if the iteration must continue. It takes as input the currently visited
 *     path, the type of that path (e.g., array or group), and the data
 *     provided by the user for the callback. The callback returns `-1` upon
 *     error. Note that `path` in the callback will be an **absolute** path.
 * @param data The data passed in the callback as the last argument.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_vfs_ls(
    tiledb_ctx_t* ctx,
    tiledb_vfs_t* vfs,
    const char* path,
    int32_t (*callback)(const char*, void*),
    void* data);

/**
 * Frees a file handle.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_vfs_fh_free(&fh);
 * @endcode
 *
 * @param fh The URI file handle.
 */
TILEDB_EXPORT void tiledb_vfs_fh_free(tiledb_vfs_fh_t** fh);

/**
 * Checks if a file handle is closed.
 *
 * **Example:**
 *
 * @code{.c}
 * int32_t is_closed;
 * tiledb_vfs_fh_is_closed(ctx, fh, &is_closed);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param fh The URI file handle.
 * @param is_closed Set to `1` if the file handle is closed, and `0` otherwise.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_vfs_fh_is_closed(
    tiledb_ctx_t* ctx, tiledb_vfs_fh_t* fh, int32_t* is_closed);

/**
 * Touches a file, i.e., creates a new empty file.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_vfs_touch(ctx, vfs, "my_empty_file");
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param vfs The virtual filesystem object.
 * @param uri The URI of the file to be created.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_vfs_touch(tiledb_ctx_t* ctx, tiledb_vfs_t* vfs, const char* uri);

/* ****************************** */
/*              URI               */
/* ****************************** */

/**
 * Converts the given file URI to a null-terminated platform-native file path.
 *
 * **Example:**
 *
 * @code{.c}
 * char path[TILEDB_MAX_PATH];
 * uint32_t length = TILEDB_MAX_PATH; // Must be set to non-zero
 * tiledb_uri_to_path(ctx, "file:///my_array", path, &length);
 * // This will set "my_array" to `path`
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param uri The URI to be converted.
 * @param path_out The buffer where the converted path string is stored.
 * @param path_length The length of the path buffer. On return, this is set to
 * the length of the converted path string, or 0 on error.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 *
 * @note The path_out buffer must be allocated according to the platform's
 * maximum path length (e.g. `TILEDB_MAX_PATH), which includes space for the
 * terminating null character.
 */
TILEDB_EXPORT int32_t tiledb_uri_to_path(
    tiledb_ctx_t* ctx, const char* uri, char* path_out, uint32_t* path_length);

/* ****************************** */
/*             Stats              */
/* ****************************** */

/**
 * Enable internal statistics gathering.
 *
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_stats_enable(void);

/**
 * Disable internal statistics gathering.
 *
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_stats_disable(void);

/**
 * Reset all internal statistics counters to 0.
 *
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_stats_reset(void);

/**
 * Dump all internal statistics counters to some output (e.g.,
 * file or stdout).
 *
 * @param out The output.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_stats_dump(FILE* out);

/**
 * Dump all internal statistics counters to an output string. The caller is
 * responsible for freeing the resulting string.
 *
 * **Example:**
 *
 * @code{.c}
 * char *stats_str;
 * tiledb_stats_dump_str(&stats_str);
 * // ...
 * tiledb_stats_free_str(&stats_str);
 * @endcode
 *
 * @param out Will be set to point to an allocated string containing the stats.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_stats_dump_str(char** out);

/**
 * Dump all raw internal statistics counters to some output (e.g.,
 * file or stdout) as a JSON.
 *
 * @param out The output.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_stats_raw_dump(FILE* out);

/**
 * Dump all raw internal statistics counters to a JSON-formatted output string.
 * The caller is responsible for freeing the resulting string.
 *
 * **Example:**
 *
 * @code{.c}
 * char *stats_str;
 * tiledb_stats_raw_dump_str(&stats_str);
 * // ...
 * tiledb_stats_raw_free_str(&stats_str);
 * @endcode
 *
 * @param out Will be set to point to an allocated string containing the stats.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_stats_raw_dump_str(char** out);

/**
 *
 * Free the memory associated with a previously dumped stats string.
 *
 * @param out Pointer to a previously allocated stats string.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_stats_free_str(char** out);

/* ****************************** */
/*          Heap Profiler         */
/* ****************************** */

/**
 * Enable heap profiling.
 *
 * @param file_name_prefix If empty or null, stats are dumped
 *   to stdout. If non-empty, this specifies the file_name prefix to
 *   write to. For example, value "tiledb_mem_stats" will write
 *   to "tiledb_mem_stats__1611170501", where the postfix is
 *   determined by the current epoch.
 * @param dump_interval_ms If non-zero, this spawns a dedicated
 *   thread to dump on this time interval.
 * @param dump_interval_bytes If non-zero, a dump will occur when
 *   the total number of lifetime allocated bytes is increased by
 *   more than this amount.
 * @param dump_threshold_bytes If non-zero, labeled allocations with
 *   a number of bytes lower than this threshold will not be reported
 *   in the dump.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_heap_profiler_enable(
    const char* file_name_prefix,
    uint64_t dump_interval_ms,
    uint64_t dump_interval_bytes,
    uint64_t dump_threshold_bytes);

/* ****************************** */
/*          FRAGMENT INFO         */
/* ****************************** */

/**
 * Creates a fragment info object for a given array, and fetches all
 * the fragment information for that array.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_fragment_info* fragment_info;
 * tiledb_fragment_info_alloc(ctx, "array_uri", &fragment_info);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param array_uri The array URI.
 * @param fragment_info The fragment info object to be created and populated.
 * @return `TILEDB_OK` for success and `TILEDB_OOM` or `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_fragment_info_alloc(
    tiledb_ctx_t* ctx,
    const char* array_uri,
    tiledb_fragment_info_t** fragment_info);

/**
 * Frees a fragment info object.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_fragment_info_free(&fragment_info);
 * @endcode
 *
 * @param fragment_info The fragment info object to be freed.
 */
TILEDB_EXPORT void tiledb_fragment_info_free(
    tiledb_fragment_info_t** fragment_info);

/**
 * Loads the fragment info.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_fragment_info_load(ctx, fragment_info);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param fragment_info The fragment info object.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_fragment_info_load(
    tiledb_ctx_t* ctx, tiledb_fragment_info_t* fragment_info);

/**
 * Loads the fragment info from an encrypted array.
 *
 * **Example:**
 *
 * @code{.c}
 * tiledb_fragment_info_load_with_key(
 *     ctx, fragment_info, TILEDB_AES_256_GCM, key, sizeof(key));
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param fragment_info The fragment info object.
 * @param encryption_type The encryption type to use.
 * @param encryption_key The encryption key to use.
 * @param key_length Length in bytes of the encryption key.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_fragment_info_load_with_key(
    tiledb_ctx_t* ctx,
    tiledb_fragment_info_t* fragment_info,
    tiledb_encryption_type_t encryption_type,
    const void* encryption_key,
    uint32_t key_length);

/**
 * Gets the number of fragments.
 *
 * **Example:**
 *
 * @code{.c}
 * uint32_t fragment_num;
 * tiledb_fragment_info_get_fragment_num(ctx, fragment_info, &fragment_num);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param fragment_info The fragment info object.
 * @param fragment_num The number of fragments to retrieve.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_fragment_info_get_fragment_num(
    tiledb_ctx_t* ctx,
    tiledb_fragment_info_t* fragment_info,
    uint32_t* fragment_num);

/**
 * Gets a fragment URI.
 *
 * **Example:**
 *
 * @code{.c}
 * const char* uri;
 * tiledb_fragment_info_get_fragment_uri(ctx, fragment_info, 1, &uri);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param fragment_info The fragment info object.
 * @param fid The index of the fragment of interest.
 * @param uri The fragment URI to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_fragment_info_get_fragment_uri(
    tiledb_ctx_t* ctx,
    tiledb_fragment_info_t* fragment_info,
    uint32_t fid,
    const char** uri);

/**
 * Gets the fragment size in bytes.
 *
 * **Example:**
 *
 * @code{.c}
 * uint64_t size;
 * tiledb_fragment_info_get_fragment_size(ctx, fragment_info, 1, &size);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param fragment_info The fragment info object.
 * @param fid The index of the fragment of interest.
 * @param size The fragment size to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_fragment_info_get_fragment_size(
    tiledb_ctx_t* ctx,
    tiledb_fragment_info_t* fragment_info,
    uint32_t fid,
    uint64_t* size);

/**
 * Checks if a fragment is dense.
 *
 * **Example:**
 *
 * @code{.c}
 * int32_t dense;
 * tiledb_fragment_info_get_dense(ctx, fragment_info, 1, &dense);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param fragment_info The fragment info object.
 * @param fid The index of the fragment of interest.
 * @param dense `1` if the fragment is dense.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_fragment_info_get_dense(
    tiledb_ctx_t* ctx,
    tiledb_fragment_info_t* fragment_info,
    uint32_t fid,
    int32_t* dense);

/**
 * Checks if a fragment is sparse.
 *
 * **Example:**
 *
 * @code{.c}
 * int32_t sparse;
 * tiledb_fragment_info_get_sparse(ctx, fragment_info, 1, &sparse);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param fragment_info The fragment info object.
 * @param fid The index of the fragment of interest.
 * @param sparse `1` if the fragment is sparse.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_fragment_info_get_sparse(
    tiledb_ctx_t* ctx,
    tiledb_fragment_info_t* fragment_info,
    uint32_t fid,
    int32_t* sparse);

/**
 * Gets the timestamp range of a fragment.
 *
 * **Example:**
 *
 * @code{.c}
 * uint64_t start, end;
 * tiledb_fragment_info_get_timestamp_range(ctx, fragment_info, 1, &start,
 * &end);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param fragment_info The fragment info object.
 * @param fid The index of the fragment of interest.
 * @param start The start of the timestamp range to be retrieved.
 * @param end The end of the timestamp range to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_fragment_info_get_timestamp_range(
    tiledb_ctx_t* ctx,
    tiledb_fragment_info_t* fragment_info,
    uint32_t fid,
    uint64_t* start,
    uint64_t* end);

/**
 * Retrieves the non-empty domain from a given fragment for a given
 * dimension index.
 *
 * **Example:**
 *
 * @code{.c}
 * uint64_t domain[2];
 * tiledb_fragment_info_get_non_empty_domain_from_index(
 *     ctx, fragment_info, 0, 0, domain);
 * @endcode
 *
 * @param ctx The TileDB context
 * @param fragment_info The fragment info object.
 * @param fid The index of the fragment of interest.
 * @param did The dimension index, following the order as it was defined
 *      in the domain of the array schema.
 * @param domain The domain to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_fragment_info_get_non_empty_domain_from_index(
    tiledb_ctx_t* ctx,
    tiledb_fragment_info_t* fragment_info,
    uint32_t fid,
    uint32_t did,
    void* domain);

/**
 * Retrieves the non-empty domain from a given fragment for a given
 * dimension name.
 *
 * **Example:**
 *
 * @code{.c}
 * uint64_t domain[2];
 * tiledb_fragment_info_get_non_empty_domain_from_name(
 *     ctx, fragment_info, 0, "d1", domain);
 * @endcode
 *
 * @param ctx The TileDB context
 * @param fragment_info The fragment info object.
 * @param fid The index of the fragment of interest.
 * @param dim_name The dimension name.
 * @param domain The domain to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_fragment_info_get_non_empty_domain_from_name(
    tiledb_ctx_t* ctx,
    tiledb_fragment_info_t* fragment_info,
    uint32_t fid,
    const char* dim_name,
    void* domain);

/**
 * Retrieves the non-empty domain range sizes from a fragment for a given
 * dimension index. Applicable to var-sized dimensions.
 *
 * **Example:**
 *
 * @code{.c}
 * uint64_t start_size, end_size;
 * tiledb_fragment_info_get_non_empty_domain_var_size_from_index(
 *     ctx, fragment_info, 0, &start_size, &end_size);
 * // If non-empty domain range is `[aa, dddd]`, then `start_size = 2`
 * // and `end_size = 4`.
 * @endcode
 *
 * @param ctx The TileDB context
 * @param fragment_info The fragment information object.
 * @param fid The fragment index.
 * @param did The dimension index, following the order as it was defined
 *      in the domain of the array schema.
 * @param start_size The size in bytes of the start range.
 * @param end_size The size in bytes of the end range.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_fragment_info_get_non_empty_domain_var_size_from_index(
    tiledb_ctx_t* ctx,
    tiledb_fragment_info_t* fragment_info,
    uint32_t fid,
    uint32_t did,
    uint64_t* start_size,
    uint64_t* end_size);

/**
 * Retrieves the non-empty domain range sizes from a fragment for a given
 * dimension name. Applicable to var-sized dimensions.
 *
 * **Example:**
 *
 * @code{.c}
 * uint64_t start_size, end_size;
 * tiledb_fragment_info_get_non_empty_domain_var_size_from_name(
 *     ctx, fragment_info, "d", &start_size, &end_size);
 * // If non-empty domain range is `[aa, dddd]`, then `start_size = 2`
 * // and `end_size = 4`.
 * @endcode
 *
 * @param ctx The TileDB context
 * @param fragment_info The fragment information object.
 * @param fid The fragment index.
 * @param dim_name The dimension name.
 * @param start_size The size in bytes of the start range.
 * @param end_size The size in bytes of the end range.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t
tiledb_fragment_info_get_non_empty_domain_var_size_from_name(
    tiledb_ctx_t* ctx,
    tiledb_fragment_info_t* fragment_info,
    uint32_t fid,
    const char* dim_name,
    uint64_t* start_size,
    uint64_t* end_size);

/**
 * Retrieves the non-empty domain from a fragment for a given
 * dimension index. Applicable to var-sized dimensions.
 *
 * **Example:**
 *
 * @code{.c}
 *
 * // Get range sizes first
 * uint64_t start_size, end_size;
 * tiledb_fragment_info_get_non_empty_domain_var_size_from_index(
 *     ctx, fragment_info, 0, 0, &start_size, &end_size);
 *
 * // Get domain
 * char start[start_size];
 * char end[end_size];
 * tiledb_fragment_info_get_non_empty_domain_var_from_index(
 *     ctx, fragment_info, 0, 0, start, end);
 * @endcode
 *
 * @param ctx The TileDB context
 * @param fragment_info The fragment info object.
 * @param fid The fragment index.
 * @param did The dimension index, following the order as it was defined
 *      in the domain of the array schema.
 * @param start The domain range start to set.
 * @param end The domain range end to set.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_fragment_info_get_non_empty_domain_var_from_index(
    tiledb_ctx_t* ctx,
    tiledb_fragment_info_t* fragment_info,
    uint32_t fid,
    uint32_t did,
    void* start,
    void* end);

/**
 * Retrieves the non-empty domain from a fragment for a given
 * dimension name. Applicable to var-sized dimensions.
 *
 * **Example:**
 *
 * @code{.c}
 *
 * // Get range sizes first
 * uint64_t start_size, end_size;
 * tiledb_fragment_info_get_non_empty_domain_var_size_from_name(
 *     ctx, fragment_info, 0, "d", &start_size, &end_size);
 *
 * // Get domain
 * char start[start_size];
 * char end[end_size];
 * tiledb_fragment_info_get_non_empty_domain_var_from_name(
 *     ctx, fragment_info, 0, "d", start, end);
 * @endcode
 *
 * @param ctx The TileDB context
 * @param fragment_info The fragment info object.
 * @param fid The fragment index.
 * @param dim_name The dimension name.
 * @param start The domain range start to set.
 * @param end The domain range end to set.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_fragment_info_get_non_empty_domain_var_from_name(
    tiledb_ctx_t* ctx,
    tiledb_fragment_info_t* fragment_info,
    uint32_t fid,
    const char* dim_name,
    void* start,
    void* end);

/**
 * Retrieves the number of cells written to the fragment by the user.
 *
 * In the case of sparse fragments, this is the number of non-empty
 * cells in the fragment.
 *
 * In the case of dense fragments, TileDB may add fill
 * values to populate partially populated tiles. Those fill values
 * are counted in the returned number of cells. In other words,
 * the cell number is derived from the number of *integral* tiles
 * written in the file.
 *
 * **Example:**
 *
 * @code{.c}
 * uint64_t cell_num;
 * tiledb_fragment_info_get_cell_num(ctx, fragment_info, 0, &cell_num);
 * @endcode
 *
 * @param ctx The TileDB context
 * @param fragment_info The fragment info object.
 * @param fid The index of the fragment of interest.
 * @param cell_num The number of cells to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_fragment_info_get_cell_num(
    tiledb_ctx_t* ctx,
    tiledb_fragment_info_t* fragment_info,
    uint32_t fid,
    uint64_t* cell_num);

/**
 * Retrieves the format version of a fragment.
 *
 * **Example:**
 *
 * @code{.c}
 * uint32_t version;
 * tiledb_fragment_info_get_version(ctx, fragment_info, 0, &version);
 * @endcode
 *
 * @param ctx The TileDB context
 * @param fragment_info The fragment info object.
 * @param fid The index of the fragment of interest.
 * @param version The format version to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_fragment_info_get_version(
    tiledb_ctx_t* ctx,
    tiledb_fragment_info_t* fragment_info,
    uint32_t fid,
    uint32_t* version);

/**
 * Checks if a fragment has consolidated metadata.
 *
 * **Example:**
 *
 * @code{.c}
 * int32_t has;
 * tiledb_fragment_info_has_consolidated_metadata(ctx, fragment_info, 0, &has);
 * @endcode
 *
 * @param ctx The TileDB context
 * @param fragment_info The fragment info object.
 * @param fid The index of the fragment of interest.
 * @param has True if the fragment has consolidated metadata.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_fragment_info_has_consolidated_metadata(
    tiledb_ctx_t* ctx,
    tiledb_fragment_info_t* fragment_info,
    uint32_t fid,
    int32_t* has);

/**
 * Gets the number of fragments with unconsolidated metadata.
 *
 * **Example:**
 *
 * @code{.c}
 * uint32_t unconsolidated;
 * tiledb_fragment_info_get_unconsolidated_metadata_num(ctx, fragment_info,
 * &unconsolidated);
 * @endcode
 *
 * @param ctx The TileDB context
 * @param fragment_info The fragment info object.
 * @param unconsolidated The number of fragments with unconsolidated metadata.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_fragment_info_get_unconsolidated_metadata_num(
    tiledb_ctx_t* ctx,
    tiledb_fragment_info_t* fragment_info,
    uint32_t* unconsolidated);

/**
 * Gets the number of fragments to vacuum.
 *
 * **Example:**
 *
 * @code{.c}
 * uint32_t to_vacuum_num;
 * tiledb_fragment_info_get_to_vacuum_num(ctx, fragment_info, &to_vacuum_num);
 * @endcode
 *
 * @param ctx The TileDB context
 * @param fragment_info The fragment info object.
 * @param to_vacuum_num The number of fragments to vacuum.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_fragment_info_get_to_vacuum_num(
    tiledb_ctx_t* ctx,
    tiledb_fragment_info_t* fragment_info,
    uint32_t* to_vacuum_num);

/**
 * Gets the URI of the fragment to vacuum with the given index.
 *
 * **Example:**
 *
 * @code{.c}
 * const char* uri;
 * tiledb_fragment_info_get_to_vacuum_uri(ctx, fragment_info, 1, &uri);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param fragment_info The fragment info object.
 * @param fid The index of the fragment to vacuum of interest.
 * @param uri The fragment URI to be retrieved.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_fragment_info_get_to_vacuum_uri(
    tiledb_ctx_t* ctx,
    tiledb_fragment_info_t* fragment_info,
    uint32_t fid,
    const char** uri);

/**
 * Dumps the fragment info in ASCII format in the selected output.
 *
 * **Example:**
 *
 * The following prints the fragment info dump in standard output.
 *
 * @code{.c}
 * tiledb_fragment_info_dump(ctx, fragment_info, stdout);
 * @endcode
 *
 * @param ctx The TileDB context.
 * @param fragment_info The fragment info object.
 * @param out The output.
 * @return `TILEDB_OK` for success and `TILEDB_ERR` for error.
 */
TILEDB_EXPORT int32_t tiledb_fragment_info_dump(
    tiledb_ctx_t* ctx, const tiledb_fragment_info_t* fragment_info, FILE* out);

#ifdef __cplusplus
}
#endif

#endif  // TILEDB_H
