diff --git a/src/libbson/doc/api.rst b/src/libbson/doc/api.rst index cc4365cae15..b85668154b4 100644 --- a/src/libbson/doc/api.rst +++ b/src/libbson/doc/api.rst @@ -25,5 +25,6 @@ API Reference bson_writer_t bson_get_monotonic_time bson_memory + binary_vector version legacy_extended_json diff --git a/src/libbson/doc/binary_vector.rst b/src/libbson/doc/binary_vector.rst new file mode 100644 index 00000000000..5891f3a029a --- /dev/null +++ b/src/libbson/doc/binary_vector.rst @@ -0,0 +1,149 @@ +:man_page: libbson_binary_vector + +BSON Binary Vector subtype +========================== + +In Libbson, we use the term *Vector* to refer to a data representation for compact storage of uniform elements, defined by the `BSON Binary Subtype 9 - Vector `_ specification. + +Libbson includes API support for Vectors: + +* The *view* APIs provide an efficient way to access elements of Vector fields that reside within :symbol:`bson_t` storage. +* Integration between *views* and other Libbson features: append, array builder, iter. +* Vectors can be converted to and from a plain BSON Array, subject to the specification's type conversion rules. + +The specification currently defines three element types, which Libbson interprets as: + +* ``int8``: signed integer elements, equivalent to C ``int8_t``. +* ``float32``: IEEE 754 floating point, 32 bits per element, least-significant byte first. After alignment and byte swapping, elements are equivalent to C ``float``. +* ``packed_bit``: single-bit integer elements, packed most-significant bit first. Accessible in packed form as C ``uint8_t`` or as unpacked elements using C ``bool``. + +Vector Views +------------ + +.. toctree:: + :titlesonly: + :maxdepth: 1 + + bson_vector_int8_view_t + bson_vector_int8_const_view_t + bson_vector_float32_view_t + bson_vector_float32_const_view_t + bson_vector_packed_bit_view_t + bson_vector_packed_bit_const_view_t + +Integration +----------- + +* Allocating Vectors inside :symbol:`bson_t`: + + .. toctree:: + :titlesonly: + :maxdepth: 1 + + bson_append_vector_int8_uninit + bson_append_vector_float32_uninit + bson_append_vector_packed_bit_uninit + +* Accessing an existing Vector via :symbol:`bson_iter_t`: + + .. code-block:: c + + #define BSON_ITER_HOLDS_VECTOR(iter) /* ... */ + #define BSON_ITER_HOLDS_VECTOR_INT8(iter) /* ... */ + #define BSON_ITER_HOLDS_VECTOR_FLOAT32(iter) /* ... */ + #define BSON_ITER_HOLDS_VECTOR_PACKED_BIT(iter) /* ... */ + + .. toctree:: + :titlesonly: + :maxdepth: 1 + + bson_vector_int8_view_from_iter + bson_vector_int8_const_view_from_iter + bson_vector_float32_view_from_iter + bson_vector_float32_const_view_from_iter + bson_vector_packed_bit_view_from_iter + bson_vector_packed_bit_const_view_from_iter + +Array Conversion +---------------- + +* Polymorphic array-from-vector: + + .. toctree:: + :titlesonly: + :maxdepth: 1 + + bson_append_array_from_vector + +* Type specific array-from-vector: + + .. toctree:: + :titlesonly: + :maxdepth: 1 + + bson_append_array_from_vector_int8 + bson_append_array_from_vector_float32 + bson_append_array_from_vector_packed_bit + +* Using :symbol:`bson_array_builder_t` for array-from-vector: + + .. toctree:: + :titlesonly: + :maxdepth: 1 + + bson_array_builder_append_vector_int8_elements + bson_array_builder_append_vector_float32_elements + bson_array_builder_append_vector_packed_bit_elements + bson_array_builder_append_vector_elements + +* Type specific vector-from-array: + + .. toctree:: + :titlesonly: + :maxdepth: 1 + + bson_append_vector_int8_from_array + bson_append_vector_float32_from_array + bson_append_vector_packed_bit_from_array + +Additional Definitions +---------------------- + +* Binary subtype: + + .. code-block:: c + + typedef enum { + BSON_SUBTYPE_VECTOR = 0x09, + /* ... */ + } bson_subtype_t; + +* Byte length of the Vector header: + + .. code-block:: c + + // Length of the required header for BSON_SUBTYPE_VECTOR, in bytes + #define BSON_VECTOR_HEADER_LEN 2 + +* Byte length for a Vector with specific element type and count: + + .. toctree:: + :titlesonly: + :maxdepth: 1 + + bson_vector_int8_binary_data_length + bson_vector_float32_binary_data_length + bson_vector_packed_bit_binary_data_length + +* Errors: + + .. code-block:: c + + // Error "domain" + #define BSON_ERROR_VECTOR 4 + + .. toctree:: + :titlesonly: + :maxdepth: 1 + + bson_vector_error_code_t diff --git a/src/libbson/doc/bson_append_array_from_vector.rst b/src/libbson/doc/bson_append_array_from_vector.rst new file mode 100644 index 00000000000..c247d889bcf --- /dev/null +++ b/src/libbson/doc/bson_append_array_from_vector.rst @@ -0,0 +1,40 @@ +:man_page: bson_append_array_from_vector + +bson_append_array_from_vector() +=============================== + +Synopsis +-------- + +.. code-block:: c + + #define BSON_APPEND_ARRAY_FROM_VECTOR(b, key, iter) \ + bson_append_array_from_vector (b, key, (int) strlen (key), iter) + + bool + bson_append_array_from_vector (bson_t *bson, + const char *key, + int key_length, + const bson_iter_t *iter); + +Parameters +---------- + +* ``bson``: A :symbol:`bson_t`. +* ``key``: An ASCII C string containing the name of the field. +* ``key_length``: The length of ``key`` in bytes, or -1 to determine the length with ``strlen()``. +* ``iter``: A :symbol:`bson_iter_t` pointing to any supported :doc:`binary_vector` field. + +Description +----------- + +Converts the Vector pointed to by ``iter`` into a plain BSON Array, written to ``bson`` under the name ``key``. + +Returns +------- + +Returns ``true`` if the operation was applied successfully. The function fails if appending the array grows ``bson`` larger than INT32_MAX, or if ``iter`` doesn't point to a valid recognized Vector type. + +.. seealso:: + + | :symbol:`bson_array_builder_append_vector_elements` diff --git a/src/libbson/doc/bson_append_array_from_vector_float32.rst b/src/libbson/doc/bson_append_array_from_vector_float32.rst new file mode 100644 index 00000000000..376a95c0bab --- /dev/null +++ b/src/libbson/doc/bson_append_array_from_vector_float32.rst @@ -0,0 +1,40 @@ +:man_page: bson_append_array_from_vector_float32 + +bson_append_array_from_vector_float32() +======================================= + +Synopsis +-------- + +.. code-block:: c + + #define BSON_APPEND_ARRAY_FROM_VECTOR_FLOAT32(b, key, view) \ + bson_append_array_from_vector_float32 (b, key, (int) strlen (key), view) + + bool + bson_append_array_from_vector_float32 (bson_t *bson, + const char *key, + int key_length, + bson_vector_float32_const_view_t view); + +Parameters +---------- + +* ``bson``: A :symbol:`bson_t`. +* ``key``: An ASCII C string containing the name of the field. +* ``key_length``: The length of ``key`` in bytes, or -1 to determine the length with ``strlen()``. +* ``view``: A :symbol:`bson_vector_float32_const_view_t` pointing to validated ``float32`` :doc:`binary_vector` data. + +Description +----------- + +Converts the Vector pointed to by ``view`` into a plain BSON Array, written to ``bson`` under the name ``key``. + +Returns +------- + +Returns ``true`` if the operation was applied successfully. The function fails if appending the array grows ``bson`` larger than INT32_MAX. + +.. seealso:: + + | :symbol:`bson_array_builder_append_vector_float32_elements` diff --git a/src/libbson/doc/bson_append_array_from_vector_int8.rst b/src/libbson/doc/bson_append_array_from_vector_int8.rst new file mode 100644 index 00000000000..d4f29b83463 --- /dev/null +++ b/src/libbson/doc/bson_append_array_from_vector_int8.rst @@ -0,0 +1,40 @@ +:man_page: bson_append_array_from_vector_int8 + +bson_append_array_from_vector_int8() +==================================== + +Synopsis +-------- + +.. code-block:: c + + #define BSON_APPEND_ARRAY_FROM_VECTOR_INT8(b, key, view) \ + bson_append_array_from_vector_int8 (b, key, (int) strlen (key), view) + + bool + bson_append_array_from_vector_int8 (bson_t *bson, + const char *key, + int key_length, + bson_vector_int8_const_view_t view); + +Parameters +---------- + +* ``bson``: A :symbol:`bson_t`. +* ``key``: An ASCII C string containing the name of the field. +* ``key_length``: The length of ``key`` in bytes, or -1 to determine the length with ``strlen()``. +* ``view``: A :symbol:`bson_vector_int8_const_view_t` pointing to validated ``int8`` :doc:`binary_vector` data. + +Description +----------- + +Converts the Vector pointed to by ``view`` into a plain BSON Array, written to ``bson`` under the name ``key``. + +Returns +------- + +Returns ``true`` if the operation was applied successfully. The function fails if appending the array grows ``bson`` larger than INT32_MAX. + +.. seealso:: + + | :symbol:`bson_array_builder_append_vector_int8_elements` diff --git a/src/libbson/doc/bson_append_array_from_vector_packed_bit.rst b/src/libbson/doc/bson_append_array_from_vector_packed_bit.rst new file mode 100644 index 00000000000..0f30355b020 --- /dev/null +++ b/src/libbson/doc/bson_append_array_from_vector_packed_bit.rst @@ -0,0 +1,40 @@ +:man_page: bson_append_array_from_vector_packed_bit + +bson_append_array_from_vector_packed_bit() +========================================== + +Synopsis +-------- + +.. code-block:: c + + #define BSON_APPEND_ARRAY_FROM_VECTOR_PACKED_BIT(b, key, view) \ + bson_append_array_from_vector_packed_bit (b, key, (int) strlen (key), view) + + bool + bson_append_array_from_vector_packed_bit (bson_t *bson, + const char *key, + int key_length, + bson_vector_packed_bit_const_view_t view); + +Parameters +---------- + +* ``bson``: A :symbol:`bson_t`. +* ``key``: An ASCII C string containing the name of the field. +* ``key_length``: The length of ``key`` in bytes, or -1 to determine the length with ``strlen()``. +* ``view``: A :symbol:`bson_vector_packed_bit_const_view_t` pointing to validated ``packed_bit`` :doc:`binary_vector` data. + +Description +----------- + +Converts the Vector pointed to by ``view`` into a plain BSON Array, written to ``bson`` under the name ``key``. + +Returns +------- + +Returns ``true`` if the operation was applied successfully. The function fails if appending the array grows ``bson`` larger than INT32_MAX. + +.. seealso:: + + | :symbol:`bson_array_builder_append_vector_packed_bit_elements` diff --git a/src/libbson/doc/bson_append_binary.rst b/src/libbson/doc/bson_append_binary.rst index c3fce9081bb..f8a7f6728be 100644 --- a/src/libbson/doc/bson_append_binary.rst +++ b/src/libbson/doc/bson_append_binary.rst @@ -38,3 +38,7 @@ Returns ------- Returns ``true`` if the operation was applied successfully. The function will fail if appending ``binary`` grows ``bson`` larger than INT32_MAX. + +.. seealso:: + + | :symbol:`bson_append_binary_uninit` diff --git a/src/libbson/doc/bson_append_binary_uninit.rst b/src/libbson/doc/bson_append_binary_uninit.rst new file mode 100644 index 00000000000..7b14213490d --- /dev/null +++ b/src/libbson/doc/bson_append_binary_uninit.rst @@ -0,0 +1,43 @@ +:man_page: bson_append_binary_uninit + +bson_append_binary_uninit() +=========================== + +Synopsis +-------- + +.. code-block:: c + + #define BSON_APPEND_BINARY_UNINIT(b, key, subtype, val, len) \ + bson_append_binary_uninit (b, key, (int) strlen (key), subtype, val, len) + + bool + bson_append_binary_uninit (bson_t *bson, + const char *key, + int key_length, + bson_subtype_t subtype, + uint8_t **binary, + uint32_t length); + +Parameters +---------- + +* ``bson``: A :symbol:`bson_t`. +* ``key``: The key name. +* ``key_length``: The length of ``key`` in bytes or -1 to use strlen(). +* ``subtype``: A bson_subtype_t indicating the binary subtype. +* ``binary``: Location for a pointer that will receive a writable pointer to the uninitialized binary data block. +* ``length``: The length of ``buffer`` in bytes. + +Description +----------- + +The :symbol:`bson_append_binary_uninit()` function is an alternative to :symbol:`bson_append_binary()` which allows applications to assemble the contents of the binary field within the :symbol:`bson_t`, without an additional temporary buffer. +On success, the caller MUST write to every byte of the binary data block if the resulting :symbol:`bson_t` is to be used. +The buffer that ``binary`` points to is only valid until the iterator's :symbol:`bson_t` is otherwise modified or freed. + + +Returns +------- + +Returns ``true`` if the uninitialized ``binary`` item was appended. The function will fail if appending ``binary`` grows ``bson`` larger than INT32_MAX. diff --git a/src/libbson/doc/bson_append_vector_float32_from_array.rst b/src/libbson/doc/bson_append_vector_float32_from_array.rst new file mode 100644 index 00000000000..76c991f84e1 --- /dev/null +++ b/src/libbson/doc/bson_append_vector_float32_from_array.rst @@ -0,0 +1,45 @@ +:man_page: bson_append_vector_float32_from_array + +bson_append_vector_float32_from_array() +======================================= + +Synopsis +-------- + +.. code-block:: c + + #define BSON_APPEND_VECTOR_FLOAT32_FROM_ARRAY(b, key, iter, err) \ + bson_append_vector_float32_from_array (b, key, (int) strlen (key), iter, err) + + bool + bson_append_vector_float32_from_array (bson_t *bson, + const char *key, + int key_length, + const bson_iter_t *iter, + bson_error_t *error); + +Parameters +---------- + +* ``bson``: A :symbol:`bson_t`. +* ``key``: An ASCII C string containing the name of the field. +* ``key_length``: The length of ``key`` in bytes, or -1 to determine the length with ``strlen()``. +* ``iter``: A :symbol:`bson_iter_t` referencing array elements that will be converted. +* ``error``: Optional :symbol:`bson_error_t` for detail about conversion failures. + +Description +----------- + +Appends a new field to ``bson`` by converting an Array to a Vector of ``float32`` elements. + +For the conversion to succeed, every item in the Array must be double-precision floating point number. (``BSON_TYPE_DOUBLE``) + +The provided ``iter`` must be positioned just prior to the first element of the BSON Array. +If your input is a bare BSON Array, set up ``iter`` using :symbol:`bson_iter_init`. +If the input is within a document field, use :symbol:`bson_iter_recurse`. + +Returns +------- + +Returns ``true`` if the operation was applied successfully. On error, returns ``false`` and writes additional error information to ``error`` without modifying ``bson``. +The error will have a ``domain`` of ``BSON_ERROR_VECTOR`` and a ``code`` from :symbol:`bson_vector_error_code_t`. diff --git a/src/libbson/doc/bson_append_vector_float32_uninit.rst b/src/libbson/doc/bson_append_vector_float32_uninit.rst new file mode 100644 index 00000000000..2a1989100e6 --- /dev/null +++ b/src/libbson/doc/bson_append_vector_float32_uninit.rst @@ -0,0 +1,42 @@ +:man_page: bson_append_vector_float32_uninit + +bson_append_vector_float32_uninit() +=================================== + +Synopsis +-------- + +.. code-block:: c + + #define BSON_APPEND_VECTOR_FLOAT32_UNINIT(b, key, count, view) \ + bson_append_vector_float32_uninit (b, key, (int) strlen (key), count, view) + + bool + bson_append_vector_float32_uninit (bson_t *bson, + const char *key, + int key_length, + size_t element_count, + bson_vector_float32_view_t *view_out); + +Parameters +---------- + +* ``bson``: A :symbol:`bson_t`. +* ``key``: An ASCII C string containing the name of the field. +* ``key_length``: The length of ``key`` in bytes, or -1 to determine the length with ``strlen()``. +* ``element_count``: Number of elements to allocate space for. +* ``view_out``: Receives a :symbol:`bson_vector_float32_view_t` with uninitialized elements. + +Description +----------- + +Appends a new field to ``bson`` by allocating a Vector with the indicated number of ``float32`` elements. +The elements will be uninitialized. +On success, the caller must write every element in the Vector if the resulting :symbol:`bson_t` is to be used. + +The view written to ``*view_out`` is only valid until ``bson`` is otherwise modified or freed. + +Returns +------- + +Returns ``true`` if the operation was applied successfully. The function fails if appending the array grows ``bson`` larger than INT32_MAX. diff --git a/src/libbson/doc/bson_append_vector_int8_from_array.rst b/src/libbson/doc/bson_append_vector_int8_from_array.rst new file mode 100644 index 00000000000..76e9efcd6d3 --- /dev/null +++ b/src/libbson/doc/bson_append_vector_int8_from_array.rst @@ -0,0 +1,46 @@ +:man_page: bson_append_vector_int8_from_array + +bson_append_vector_int8_from_array() +==================================== + +Synopsis +-------- + +.. code-block:: c + + #define BSON_APPEND_VECTOR_INT8_FROM_ARRAY(b, key, iter, err) \ + bson_append_vector_int8_from_array (b, key, (int) strlen (key), iter, err) + + bool + bson_append_vector_int8_from_array (bson_t *bson, + const char *key, + int key_length, + const bson_iter_t *iter, + bson_error_t *error); + +Parameters +---------- + +* ``bson``: A :symbol:`bson_t`. +* ``key``: An ASCII C string containing the name of the field. +* ``key_length``: The length of ``key`` in bytes, or -1 to determine the length with ``strlen()``. +* ``iter``: A :symbol:`bson_iter_t` referencing array elements that will be converted. +* ``error``: Optional :symbol:`bson_error_t` for detail about conversion failures. + +Description +----------- + +Appends a new field to ``bson`` by converting an Array to a Vector of ``int8`` elements. + +For the conversion to succeed, every item in the Array must be an integer (``BSON_TYPE_INT32`` or ``BSON_TYPE_INT64``) in the range ``INT8_MIN`` (-128) through ``INT8_MAX`` (127) inclusive. +If any element has an incorrect type or an out-of-range value, the conversion fails with an ``error`` message providing details, and no changes are made to ``bson``. + +The provided ``iter`` must be positioned just prior to the first element of the BSON Array. +If your input is a bare BSON Array, set up ``iter`` using :symbol:`bson_iter_init`. +If the input is within a document field, use :symbol:`bson_iter_recurse`. + +Returns +------- + +Returns ``true`` if the operation was applied successfully. On error, returns ``false`` and writes additional error information to ``error`` without modifying ``bson``. +The error will have a ``domain`` of ``BSON_ERROR_VECTOR`` and a ``code`` from :symbol:`bson_vector_error_code_t`. diff --git a/src/libbson/doc/bson_append_vector_int8_uninit.rst b/src/libbson/doc/bson_append_vector_int8_uninit.rst new file mode 100644 index 00000000000..62234f5c632 --- /dev/null +++ b/src/libbson/doc/bson_append_vector_int8_uninit.rst @@ -0,0 +1,42 @@ +:man_page: bson_append_vector_int8_uninit + +bson_append_vector_int8_uninit() +================================ + +Synopsis +-------- + +.. code-block:: c + + #define BSON_APPEND_VECTOR_INT8_UNINIT(b, key, count, view) \ + bson_append_vector_int8_uninit (b, key, (int) strlen (key), count, view) + + bool + bson_append_vector_int8_uninit (bson_t *bson, + const char *key, + int key_length, + size_t element_count, + bson_vector_int8_view_t *view_out); + +Parameters +---------- + +* ``bson``: A :symbol:`bson_t`. +* ``key``: An ASCII C string containing the name of the field. +* ``key_length``: The length of ``key`` in bytes, or -1 to determine the length with ``strlen()``. +* ``element_count``: Number of elements to allocate space for. +* ``view_out``: Receives a :symbol:`bson_vector_int8_view_t` with uninitialized elements. + +Description +----------- + +Appends a new field to ``bson`` by allocating a Vector with the indicated number of ``int8`` elements. +The elements will be uninitialized. +On success, the caller must write every element in the Vector if the resulting :symbol:`bson_t` is to be used. + +The view written to ``*view_out`` is only valid until ``bson`` is otherwise modified or freed. + +Returns +------- + +Returns ``true`` if the operation was applied successfully. The function fails if appending the array grows ``bson`` larger than INT32_MAX. diff --git a/src/libbson/doc/bson_append_vector_packed_bit_from_array.rst b/src/libbson/doc/bson_append_vector_packed_bit_from_array.rst new file mode 100644 index 00000000000..fc767243a4c --- /dev/null +++ b/src/libbson/doc/bson_append_vector_packed_bit_from_array.rst @@ -0,0 +1,46 @@ +:man_page: bson_append_vector_packed_bit_from_array + +bson_append_vector_packed_bit_from_array() +========================================== + +Synopsis +-------- + +.. code-block:: c + + #define BSON_APPEND_VECTOR_PACKED_BIT_FROM_ARRAY(b, key, iter, err) \ + bson_append_vector_packed_bit_from_array (b, key, (int) strlen (key), iter, err) + + bool + bson_append_vector_packed_bit_from_array (bson_t *bson, + const char *key, + int key_length, + const bson_iter_t *iter, + bson_error_t *error); + +Parameters +---------- + +* ``bson``: A :symbol:`bson_t`. +* ``key``: An ASCII C string containing the name of the field. +* ``key_length``: The length of ``key`` in bytes, or -1 to determine the length with ``strlen()``. +* ``iter``: A :symbol:`bson_iter_t` referencing array elements that will be converted. +* ``error``: Optional :symbol:`bson_error_t` for detail about conversion failures. + +Description +----------- + +Appends a new field to ``bson`` by converting an Array to a Vector of ``packed_bit`` elements. + +For the conversion to succeed, every item in the Array must be either an integer (``BSON_TYPE_INT32`` or ``BSON_TYPE_INT64``) with the values ``0`` or ``1``, or boolean (``BSON_TYPE_BOOL``). +If any element has an incorrect type or an out-of-range value, the conversion fails with an ``error`` message providing details, and no changes are made to ``bson``. + +The provided ``iter`` must be positioned just prior to the first element of the BSON Array. +If your input is a bare BSON Array, set up ``iter`` using :symbol:`bson_iter_init`. +If the input is within a document field, use :symbol:`bson_iter_recurse`. + +Returns +------- + +Returns ``true`` if the operation was applied successfully. On error, returns ``false`` and writes additional error information to ``error`` without modifying ``bson``. +The error will have a ``domain`` of ``BSON_ERROR_VECTOR`` and a ``code`` from :symbol:`bson_vector_error_code_t`. diff --git a/src/libbson/doc/bson_append_vector_packed_bit_uninit.rst b/src/libbson/doc/bson_append_vector_packed_bit_uninit.rst new file mode 100644 index 00000000000..40ef02ecc50 --- /dev/null +++ b/src/libbson/doc/bson_append_vector_packed_bit_uninit.rst @@ -0,0 +1,42 @@ +:man_page: bson_append_vector_packed_bit_uninit + +bson_append_vector_packed_bit_uninit() +====================================== + +Synopsis +-------- + +.. code-block:: c + + #define BSON_APPEND_VECTOR_PACKED_BIT_UNINIT(b, key, count, view) \ + bson_append_vector_packed_bit_uninit (b, key, (int) strlen (key), count, view) + + bool + bson_append_vector_packed_bit_uninit (bson_t *bson, + const char *key, + int key_length, + size_t element_count, + bson_vector_packed_bit_view_t *view_out); + +Parameters +---------- + +* ``bson``: A :symbol:`bson_t`. +* ``key``: An ASCII C string containing the name of the field. +* ``key_length``: The length of ``key`` in bytes, or -1 to determine the length with ``strlen()``. +* ``element_count``: Number of elements to allocate space for. +* ``view_out``: Receives a :symbol:`bson_vector_packed_bit_view_t` with uninitialized elements. + +Description +----------- + +Appends a new field to ``bson`` by allocating a Vector with the indicated number of ``packed_bit`` elements. +The elements will be uninitialized. +On success, the caller must write every element in the Vector if the resulting :symbol:`bson_t` is to be used. + +The view written to ``*view_out`` is only valid until ``bson`` is otherwise modified or freed. + +Returns +------- + +Returns ``true`` if the operation was applied successfully. The function fails if appending the array grows ``bson`` larger than INT32_MAX. diff --git a/src/libbson/doc/bson_array_builder_append_vector_elements.rst b/src/libbson/doc/bson_array_builder_append_vector_elements.rst new file mode 100644 index 00000000000..101e24eca78 --- /dev/null +++ b/src/libbson/doc/bson_array_builder_append_vector_elements.rst @@ -0,0 +1,38 @@ +:man_page: bson_array_builder_append_vector_elements + +bson_array_builder_append_vector_elements() +=========================================== + +Synopsis +-------- + +.. code-block:: c + + bool + bson_array_builder_append_vector_elements (bson_array_builder_t *builder, + const bson_iter_t *iter); + +Parameters +---------- + +* ``builder``: A valid :symbol:`bson_array_builder_t`. +* ``iter``: A :symbol:`bson_iter_t` pointing to any supported :doc:`binary_vector` field. + +Description +----------- + +Converts the Vector pointed to by ``iter`` into elements of a plain BSON Array, written to ``builder``. +This conversion is polymorphic: A converted element type will be chosen based on the type of the input Vector. +For details, see the type-specific versions of this function. + +Returns +------- + +Returns ``true`` if the operation was applied successfully. The function fails if appending the array grows ``bson`` larger than INT32_MAX, or if ``iter`` doesn't point to a valid recognized Vector type. + +.. seealso:: + + | :symbol:`bson_append_array_from_vector` + | :symbol:`bson_array_builder_append_vector_int8_elements` + | :symbol:`bson_array_builder_append_vector_float32_elements` + | :symbol:`bson_array_builder_append_vector_packed_bit_elements` diff --git a/src/libbson/doc/bson_array_builder_append_vector_float32_elements.rst b/src/libbson/doc/bson_array_builder_append_vector_float32_elements.rst new file mode 100644 index 00000000000..cc798474aab --- /dev/null +++ b/src/libbson/doc/bson_array_builder_append_vector_float32_elements.rst @@ -0,0 +1,35 @@ +:man_page: bson_array_builder_append_vector_float32_elements + +bson_array_builder_append_vector_float32_elements() +=================================================== + +Synopsis +-------- + +.. code-block:: c + + bool + bson_array_builder_append_vector_float32_elements (bson_array_builder_t *builder, + bson_vector_float32_const_view_t view); + +Parameters +---------- + +* ``builder``: A valid :symbol:`bson_array_builder_t`. +* ``view``: A :symbol:`bson_vector_float32_const_view_t` pointing to validated ``float32`` :doc:`binary_vector` data. + +Description +----------- + +Converts the Vector pointed to by ``view`` into elements of a plain BSON Array, written to ``builder``. +Every element will be converted from ``float`` to ``double`` precision. + +Returns +------- + +Returns ``true`` if the operation was applied successfully. The function fails if appending the array grows ``bson`` larger than INT32_MAX. + +.. seealso:: + + | :symbol:`bson_append_array_from_vector` + | :symbol:`bson_array_builder_append_vector_elements` diff --git a/src/libbson/doc/bson_array_builder_append_vector_int8_elements.rst b/src/libbson/doc/bson_array_builder_append_vector_int8_elements.rst new file mode 100644 index 00000000000..477835309ab --- /dev/null +++ b/src/libbson/doc/bson_array_builder_append_vector_int8_elements.rst @@ -0,0 +1,35 @@ +:man_page: bson_array_builder_append_vector_int8_elements + +bson_array_builder_append_vector_int8_elements() +================================================ + +Synopsis +-------- + +.. code-block:: c + + bool + bson_array_builder_append_vector_int8_elements (bson_array_builder_t *builder, + bson_vector_int8_const_view_t view); + +Parameters +---------- + +* ``builder``: A valid :symbol:`bson_array_builder_t`. +* ``view``: A :symbol:`bson_vector_int8_const_view_t` pointing to validated ``int8`` :doc:`binary_vector` data. + +Description +----------- + +Converts the Vector pointed to by ``view`` into elements of a plain BSON Array, written to ``builder``. +Every element will be losslessly extended from ``int8_t`` to ``int32_t``. + +Returns +------- + +Returns ``true`` if the operation was applied successfully. The function fails if appending the array grows ``bson`` larger than INT32_MAX. + +.. seealso:: + + | :symbol:`bson_append_array_from_vector` + | :symbol:`bson_array_builder_append_vector_elements` diff --git a/src/libbson/doc/bson_array_builder_append_vector_packed_bit_elements.rst b/src/libbson/doc/bson_array_builder_append_vector_packed_bit_elements.rst new file mode 100644 index 00000000000..146fe53db6e --- /dev/null +++ b/src/libbson/doc/bson_array_builder_append_vector_packed_bit_elements.rst @@ -0,0 +1,35 @@ +:man_page: bson_array_builder_append_vector_packed_bit_elements + +bson_array_builder_append_vector_packed_bit_elements() +====================================================== + +Synopsis +-------- + +.. code-block:: c + + bool + bson_array_builder_append_vector_packed_bit_elements (bson_array_builder_t *builder, + bson_vector_packed_bit_const_view_t view); + +Parameters +---------- + +* ``builder``: A valid :symbol:`bson_array_builder_t`. +* ``view``: A :symbol:`bson_vector_packed_bit_const_view_t` pointing to validated ``packed_bit`` :doc:`binary_vector` data. + +Description +----------- + +Converts the Vector pointed to by ``view`` into elements of a plain BSON Array, written to ``builder``. +Every element will be ``0`` or ``1`` written as a ``BSON_TYPE_INT32``. + +Returns +------- + +Returns ``true`` if the operation was applied successfully. The function fails if appending the array grows ``bson`` larger than INT32_MAX. + +.. seealso:: + + | :symbol:`bson_append_array_from_vector` + | :symbol:`bson_array_builder_append_vector_elements` diff --git a/src/libbson/doc/bson_array_builder_t.rst b/src/libbson/doc/bson_array_builder_t.rst index 5baca2d7f4f..773738f5536 100644 --- a/src/libbson/doc/bson_array_builder_t.rst +++ b/src/libbson/doc/bson_array_builder_t.rst @@ -82,6 +82,12 @@ Appending values to an array const uint8_t *binary, uint32_t length); + bool + bson_array_builder_append_binary_uninit (bson_array_builder_t *bab, + bson_subtype_t subtype, + uint8_t **binary, + uint32_t length); + bool bson_array_builder_append_bool (bson_array_builder_t *bab, bool value); diff --git a/src/libbson/doc/bson_iter_binary.rst b/src/libbson/doc/bson_iter_binary.rst index b456bfbe62b..a0e408b2af2 100644 --- a/src/libbson/doc/bson_iter_binary.rst +++ b/src/libbson/doc/bson_iter_binary.rst @@ -32,3 +32,7 @@ This function shall return the binary data of a BSON_TYPE_BINARY element. It is The buffer that ``binary`` points to is only valid until the iterator's :symbol:`bson_t` is modified or freed. +.. seealso:: + + | :symbol:`bson_iter_binary_equal` + | :symbol:`bson_iter_binary_subtype` diff --git a/src/libbson/doc/bson_iter_binary_equal.rst b/src/libbson/doc/bson_iter_binary_equal.rst new file mode 100644 index 00000000000..13e6284de31 --- /dev/null +++ b/src/libbson/doc/bson_iter_binary_equal.rst @@ -0,0 +1,30 @@ +:man_page: bson_iter_binary_equal + +bson_iter_binary_equal() +======================== + +Synopsis +-------- + +.. code-block:: c + + bool + bson_iter_binary_equal (const bson_iter_t *iter_a, const bson_iter_t *iter_b); + +Parameters +---------- + +* ``iter_a``: A :symbol:`bson_iter_t`. +* ``iter_b``: A :symbol:`bson_iter_t`. + +Description +----------- + +Compare two BSON_TYPE_BINARY fields for exact equality. + +This is the preferred way to compare :doc:`binary_vector` values for equality. + +Returns +------- + +``true`` if both iterators point to BSON_TYPE_BINARY fields with identical subtype and contents. ``false`` if there is any difference in subtype, length, or content, or if the fields are not binary type. \ No newline at end of file diff --git a/src/libbson/doc/bson_iter_binary_subtype.rst b/src/libbson/doc/bson_iter_binary_subtype.rst new file mode 100644 index 00000000000..615e4234a25 --- /dev/null +++ b/src/libbson/doc/bson_iter_binary_subtype.rst @@ -0,0 +1,24 @@ +:man_page: bson_iter_binary_subtype + +bson_iter_binary_subtype() +========================== + +Synopsis +-------- + +.. code-block:: c + + bson_subtype_t + bson_iter_binary_subtype (const bson_iter_t *iter); + +Parameters +---------- + +* ``iter``: A :symbol:`bson_iter_t`. + +Description +----------- + +This function shall return the subtype of a BSON_TYPE_BINARY element. It is a programming error to call this function on a field that is not of type BSON_TYPE_BINARY. You can check this with the BSON_ITER_HOLDS_BINARY() macro or :symbol:`bson_iter_type()`. + +Equivalent to the ``subtype`` output parameter of :symbol:`bson_iter_binary`. diff --git a/src/libbson/doc/bson_iter_overwrite_binary.rst b/src/libbson/doc/bson_iter_overwrite_binary.rst new file mode 100644 index 00000000000..a41385f327c --- /dev/null +++ b/src/libbson/doc/bson_iter_overwrite_binary.rst @@ -0,0 +1,38 @@ +:man_page: bson_iter_overwrite_binary + +bson_iter_overwrite_binary() +============================ + +Synopsis +-------- + +.. code-block:: c + + void + bson_iter_overwrite_binary (bson_iter_t *iter, + bson_subtype_t subtype, + uint32_t *binary_len, + uint8_t **binary); + +Parameters +---------- + +* ``iter``: A :symbol:`bson_iter_t`. +* ``subtype``: The expected :symbol:`bson_subtype_t`. +* ``binary_len``: A location for the length of ``binary``. +* ``binary``: A location for a pointer to the mutable buffer. + +Description +----------- + +The ``bson_iter_overwrite_binary()`` function obtains mutable access to a BSON_TYPE_BINARY element in place. + +This may only be done when the underlying bson document allows mutation. + +It is a programming error to call this function when ``iter`` is not observing an element of type BSON_TYPE_BINARY and the provided ``subtype``. + +The buffer that ``binary`` points to is only valid until the iterator's :symbol:`bson_t` is otherwise modified or freed. + +.. seealso:: + + | :symbol:`bson_iter_binary` diff --git a/src/libbson/doc/bson_iter_t.rst b/src/libbson/doc/bson_iter_t.rst index 620388f7758..dd43318f68d 100644 --- a/src/libbson/doc/bson_iter_t.rst +++ b/src/libbson/doc/bson_iter_t.rst @@ -22,6 +22,11 @@ Synopsis #define BSON_ITER_HOLDS_BINARY(iter) /* ... */ + #define BSON_ITER_HOLDS_VECTOR(iter) /* ... */ + #define BSON_ITER_HOLDS_VECTOR_INT8(iter) /* ... */ + #define BSON_ITER_HOLDS_VECTOR_FLOAT32(iter) /* ... */ + #define BSON_ITER_HOLDS_VECTOR_PACKED_BIT(iter) /* ... */ + #define BSON_ITER_HOLDS_UNDEFINED(iter) /* ... */ #define BSON_ITER_HOLDS_OID(iter) /* ... */ @@ -88,6 +93,8 @@ The :symbol:`bson_t` *MUST* be valid for the lifetime of the iter and it is an e bson_iter_as_double bson_iter_as_int64 bson_iter_binary + bson_iter_binary_subtype + bson_iter_binary_equal bson_iter_bool bson_iter_code bson_iter_codewscope @@ -115,6 +122,7 @@ The :symbol:`bson_t` *MUST* be valid for the lifetime of the iter and it is an e bson_iter_offset bson_iter_oid bson_iter_overwrite_bool + bson_iter_overwrite_binary bson_iter_overwrite_date_time bson_iter_overwrite_decimal128 bson_iter_overwrite_double diff --git a/src/libbson/doc/bson_subtype_t.rst b/src/libbson/doc/bson_subtype_t.rst index 9c54a395c15..177c02203bc 100644 --- a/src/libbson/doc/bson_subtype_t.rst +++ b/src/libbson/doc/bson_subtype_t.rst @@ -22,6 +22,7 @@ Synopsis BSON_SUBTYPE_MD5 = 0x05, BSON_SUBTYPE_COLUMN = 0x07, BSON_SUBTYPE_SENSITIVE = 0x08, + BSON_SUBTYPE_VECTOR = 0x09, BSON_SUBTYPE_USER = 0x80, } bson_subtype_t; diff --git a/src/libbson/doc/bson_t.rst b/src/libbson/doc/bson_t.rst index 910698d639e..03cb80e1b04 100644 --- a/src/libbson/doc/bson_t.rst +++ b/src/libbson/doc/bson_t.rst @@ -56,6 +56,9 @@ Synopsis #define BSON_APPEND_BINARY(b, key, subtype, val, len) \ bson_append_binary (b, key, (int) strlen (key), subtype, val, len) + #define BSON_APPEND_BINARY_UNINIT(b, key, subtype, val, len) \ + bson_append_binary_uninit (b, key, (int) strlen (key), subtype, val, len) + #define BSON_APPEND_BOOL(b, key, val) \ bson_append_bool (b, key, (int) strlen (key), val) @@ -163,6 +166,7 @@ BSON document contains duplicate keys. bson_append_array_begin bson_append_array_end bson_append_binary + bson_append_binary_uninit bson_append_bool bson_append_code bson_append_code_with_scope diff --git a/src/libbson/doc/bson_vector_error_code_t.rst b/src/libbson/doc/bson_vector_error_code_t.rst new file mode 100644 index 00000000000..2e7b63db5b0 --- /dev/null +++ b/src/libbson/doc/bson_vector_error_code_t.rst @@ -0,0 +1,30 @@ +:man_page: bson_vector_error_code_t + +bson_vector_error_code_t +======================== + +BSON Error codes for :doc:`binary_vector` operations that could fail in multiple ways. + +Synopsis +-------- + +.. code-block:: c + + #define BSON_ERROR_VECTOR 4 + + typedef enum { + BSON_VECTOR_ERROR_ARRAY_ELEMENT_TYPE = 1, + BSON_VECTOR_ERROR_ARRAY_ELEMENT_VALUE = 2, + BSON_VECTOR_ERROR_ARRAY_KEY = 3, + BSON_VECTOR_ERROR_MAX_SIZE = 4, + } bson_vector_error_code_t; + +Description +----------- + +The error ``code`` values in ``bson_vector_error_code_t`` apply to :symbol:`bson_error_t` values with a ``category`` of ``BSON_ERROR_CATEGORY`` and a ``domain`` of ``BSON_ERROR_VECTOR``. + +* ``BSON_VECTOR_ERROR_ARRAY_ELEMENT_TYPE``: An element was encountered with incorrect type. Location and type details in ``message``. +* ``BSON_VECTOR_ERROR_ARRAY_ELEMENT_VALUE``: An element was encountered with out-of-range value. Location and value details in ``message``. +* ``BSON_VECTOR_ERROR_ARRAY_KEY``: An input BSON Array did not contain the expected numeric key value. Expected and actual keys in ``message``. +* ``BSON_VECTOR_ERROR_MAX_SIZE``: The BSON maximum document size would be exceeded. Equivalent to a failure from ``bson_append_*`` functions that do not return an ``error``. diff --git a/src/libbson/doc/bson_vector_float32_binary_data_length.rst b/src/libbson/doc/bson_vector_float32_binary_data_length.rst new file mode 100644 index 00000000000..77676801efa --- /dev/null +++ b/src/libbson/doc/bson_vector_float32_binary_data_length.rst @@ -0,0 +1,31 @@ +:man_page: bson_vector_float32_binary_data_length + +bson_vector_float32_binary_data_length() +======================================== + +Calculate the size of a BSON Binary field that would be needed to store a Vector with the indicated number of ``float32`` elements. + +Synopsis +-------- + +.. code-block:: c + + uint32_t + bson_vector_float32_binary_data_length (size_t element_count); + +Parameters +---------- + +* ``element_count``: Number of elements, as a ``size_t``. + +Description +----------- + +Checks ``element_count`` against the maximum representable size, and calculates the required Binary size. + +Returns +------- + +On success, returns the required Binary size as a ``uint32_t`` greater than or equal to ``BSON_VECTOR_HEADER_LEN``. +This length includes the 2-byte Vector header, but not the Binary subtype header or any other BSON headers. +If the ``element_count`` is too large to represent, returns 0. diff --git a/src/libbson/doc/bson_vector_float32_const_view_from_iter.rst b/src/libbson/doc/bson_vector_float32_const_view_from_iter.rst new file mode 100644 index 00000000000..5a1934be727 --- /dev/null +++ b/src/libbson/doc/bson_vector_float32_const_view_from_iter.rst @@ -0,0 +1,37 @@ +:man_page: bson_vector_float32_const_view_from_iter + +bson_vector_float32_const_view_from_iter() +========================================== + +Initialize a :symbol:`bson_vector_float32_const_view_t` from a :symbol:`bson_iter_t` pointing to a valid Vector of ``float32`` element type. + +Synopsis +-------- + +.. code-block:: c + + bool + bson_vector_float32_const_view_from_iter (bson_vector_float32_const_view_t *view_out, + const bson_iter_t *iter); + +Parameters +---------- + +* ``view_out``: A :symbol:`bson_vector_float32_const_view_t` is written here on success. +* ``iter``: A valid :symbol:`bson_iter_t`. + +Description +----------- + +The provided iterator, which must point to some kind of BSON item, will be checked for a valid Vector of ``float32`` element type. +On success, a :symbol:`bson_vector_float32_const_view_t` is set to point to the same underlying :symbol:`bson_t` buffer as the provided :symbol:`bson_iter_t`. +The view will only be valid until the containing document is destroyed or modified. + +Returns +------- + +Returns true if the view was successfully initialized. + +.. seealso:: + + | :symbol:`bson_vector_float32_view_from_iter` diff --git a/src/libbson/doc/bson_vector_float32_const_view_init.rst b/src/libbson/doc/bson_vector_float32_const_view_init.rst new file mode 100644 index 00000000000..db8f872d154 --- /dev/null +++ b/src/libbson/doc/bson_vector_float32_const_view_init.rst @@ -0,0 +1,39 @@ +:man_page: bson_vector_float32_const_view_init + +bson_vector_float32_const_view_init() +===================================== + +Initialize a :symbol:`bson_vector_float32_const_view_t` from a const ``uint8_t`` buffer. + +Synopsis +-------- + +.. code-block:: c + + bool + bson_vector_float32_const_view_init (bson_vector_float32_const_view_t *view_out, + const uint8_t *binary_data, + uint32_t binary_data_len); + +Parameters +---------- + +* ``view_out``: A :symbol:`bson_vector_float32_const_view_t` is written here on success. +* ``binary_data``: A pointer to the BSON Binary data block to be validated. +* ``binary_data_len``: Length of the binary data block, in bytes. + +Description +----------- + +The length and header of the provided binary data block will be checked for a valid Vector of ``float32`` element type. +On success, the pointer and length are packaged as a :symbol:`bson_vector_float32_const_view_t` written to ``*view_out``. +The view will only be valid as long as ``binary_data`` is valid. + +Returns +------- + +Returns true if the view was successfully initialized. + +.. seealso:: + + | :symbol:`bson_vector_float32_view_init` diff --git a/src/libbson/doc/bson_vector_float32_const_view_length.rst b/src/libbson/doc/bson_vector_float32_const_view_length.rst new file mode 100644 index 00000000000..e7288377eb5 --- /dev/null +++ b/src/libbson/doc/bson_vector_float32_const_view_length.rst @@ -0,0 +1,33 @@ +:man_page: bson_vector_float32_const_view_length + +bson_vector_float32_const_view_length() +======================================= + +Return the number of elements in a Vector referenced by a :symbol:`bson_vector_float32_const_view_t`. + +Synopsis +-------- + +.. code-block:: c + + size_t + bson_vector_float32_const_view_length (bson_vector_float32_const_view_t view); + +Parameters +---------- + +* ``view``: A valid :symbol:`bson_vector_float32_const_view_t`. + +Description +----------- + +An element count is calculated from the view's stored binary block length. + +Returns +------- + +The number of elements, as a ``size_t``. + +.. seealso:: + + | :symbol:`bson_vector_float32_view_length` diff --git a/src/libbson/doc/bson_vector_float32_const_view_read.rst b/src/libbson/doc/bson_vector_float32_const_view_read.rst new file mode 100644 index 00000000000..536d70c6b6f --- /dev/null +++ b/src/libbson/doc/bson_vector_float32_const_view_read.rst @@ -0,0 +1,41 @@ +:man_page: bson_vector_float32_const_view_read + +bson_vector_float32_const_view_read() +===================================== + +Copy a contiguous block of elements from a :symbol:`bson_vector_float32_const_view_t` into a C array of ``float``. + +Synopsis +-------- + +.. code-block:: c + + bool + bson_vector_float32_const_view_read (bson_vector_float32_const_view_t view, + float *values_out, + size_t element_count, + size_t vector_offset_elements); + +Parameters +---------- + +* ``view``: A valid :symbol:`bson_vector_float32_const_view_t`. +* ``values_out``: Location where the ``float`` elements will be read to. +* ``element_count``: Number of elements to read. +* ``vector_offset_elements``: The vector index of the first element to read. + +Description +----------- + +Elements are copied in bulk from the view to the provided output pointer. + +Returns +------- + +If the ``element_count`` and ``vector_offset_elements`` parameters overflow the bounds of the Vector, returns false without taking any other action. +If the parameters are in range, this is guaranteed to succeed. +On success, returns true and reads ``element_count`` elements into ``*values_out``. + +.. seealso:: + + | :symbol:`bson_vector_float32_view_read` diff --git a/src/libbson/doc/bson_vector_float32_const_view_t.rst b/src/libbson/doc/bson_vector_float32_const_view_t.rst new file mode 100644 index 00000000000..94102e731d9 --- /dev/null +++ b/src/libbson/doc/bson_vector_float32_const_view_t.rst @@ -0,0 +1,62 @@ +:man_page: bson_vector_float32_const_view_t + +bson_vector_float32_const_view_t +================================ + +A reference to non-owned const BSON Binary data holding a valid Vector of ``float32`` element type. + +Synopsis +-------- + +.. code-block:: c + + #include + + typedef struct bson_vector_float32_const_view_t { + /*< private >*/ + } bson_vector_float32_const_view_t; + +Description +----------- + +:symbol:`bson_vector_float32_const_view_t` is a structure that acts as an opaque const reference to a block of memory that has been validated as a ``float32`` vector. + +It is meant to be passed by value and can be discarded at any time. The contents of the structure should be considered private. + +The :symbol:`bson_t` *MUST* be valid for the lifetime of the view and it is an error to modify the :symbol:`bson_t` while using the view. + +.. only:: html + + Functions + --------- + + .. toctree:: + :titlesonly: + :maxdepth: 1 + + bson_vector_float32_const_view_init + bson_vector_float32_const_view_from_iter + bson_vector_float32_const_view_length + bson_vector_float32_const_view_read + +Example +------- + +.. code-block:: c + + bson_iter_t iter; + bson_vector_float32_const_view_t view; + + if (bson_iter_init_find (&iter, &doc, "vector") && bson_vector_float32_const_view_from_iter (&view, &iter)) { + size_t length = bson_vector_float32_const_view_length (view); + printf ("Elements in 'vector':\n"); + for (size_t i = 0; i < length; i++) { + float element; + BSON_ASSERT (bson_vector_float32_const_view_read (view, &element, 1, i)); + printf (" [%d] = %f\n", (int) i, element); + } + } + +.. seealso:: + + | :symbol:`bson_vector_float32_view_t` diff --git a/src/libbson/doc/bson_vector_float32_view_as_const.rst b/src/libbson/doc/bson_vector_float32_view_as_const.rst new file mode 100644 index 00000000000..f86774c5a9d --- /dev/null +++ b/src/libbson/doc/bson_vector_float32_view_as_const.rst @@ -0,0 +1,29 @@ +:man_page: bson_vector_float32_view_as_const + +bson_vector_float32_view_as_const() +=================================== + +Convert a :symbol:`bson_vector_float32_view_t` into a :symbol:`bson_vector_float32_const_view_t`. + +Synopsis +-------- + +.. code-block:: c + + bson_vector_float32_const_view_t + bson_vector_float32_view_as_const (bson_vector_float32_view_t view); + +Parameters +---------- + +* ``view``: A valid :symbol:`bson_vector_float32_view_t`. + +Description +----------- + +This adds a ``const`` qualifier to the view without re-validating the underlying data. + +Returns +------- + +Always returns a :symbol:`bson_vector_float32_const_view_t`. diff --git a/src/libbson/doc/bson_vector_float32_view_from_iter.rst b/src/libbson/doc/bson_vector_float32_view_from_iter.rst new file mode 100644 index 00000000000..8c2b0a9dcae --- /dev/null +++ b/src/libbson/doc/bson_vector_float32_view_from_iter.rst @@ -0,0 +1,37 @@ +:man_page: bson_vector_float32_view_from_iter + +bson_vector_float32_view_from_iter() +==================================== + +Initialize a :symbol:`bson_vector_float32_view_t` from a :symbol:`bson_iter_t` pointing to a valid Vector of ``float32`` element type. + +Synopsis +-------- + +.. code-block:: c + + bool + bson_vector_float32_view_from_iter (bson_vector_float32_view_t *view_out, + bson_iter_t *iter); + +Parameters +---------- + +* ``view_out``: A :symbol:`bson_vector_float32_view_t` is written here on success. +* ``iter``: A valid :symbol:`bson_iter_t`. + +Description +----------- + +The provided iterator, which must point to some kind of BSON item, will be checked for a valid Vector of ``float32`` element type. +On success, a :symbol:`bson_vector_float32_view_t` is set to point to the same underlying :symbol:`bson_t` buffer as the provided :symbol:`bson_iter_t`. +The view will only be valid until the containing document is destroyed or otherwise modified. + +Returns +------- + +Returns true if the view was successfully initialized. + +.. seealso:: + + | :symbol:`bson_vector_float32_const_view_from_iter` diff --git a/src/libbson/doc/bson_vector_float32_view_init.rst b/src/libbson/doc/bson_vector_float32_view_init.rst new file mode 100644 index 00000000000..77d0aab630a --- /dev/null +++ b/src/libbson/doc/bson_vector_float32_view_init.rst @@ -0,0 +1,39 @@ +:man_page: bson_vector_float32_view_init + +bson_vector_float32_view_init() +=============================== + +Initialize a :symbol:`bson_vector_float32_view_t` from a mutable ``uint8_t`` buffer. + +Synopsis +-------- + +.. code-block:: c + + bool + bson_vector_float32_view_init (bson_vector_float32_view_t *view_out, + uint8_t *binary_data, + uint32_t binary_data_len); + +Parameters +---------- + +* ``view_out``: A :symbol:`bson_vector_float32_view_t` is written here on success. +* ``binary_data``: A pointer to the BSON Binary data block to be validated. +* ``binary_data_len``: Length of the binary data block, in bytes. + +Description +----------- + +The length and header of the provided binary data block will be checked for a valid Vector of ``float32`` element type. +On success, the pointer and length are packaged as a :symbol:`bson_vector_float32_view_t` written to ``*view_out``. +The view will only be valid as long as ``binary_data`` is valid. + +Returns +------- + +Returns true if the view was successfully initialized. + +.. seealso:: + + | :symbol:`bson_vector_float32_const_view_init` diff --git a/src/libbson/doc/bson_vector_float32_view_length.rst b/src/libbson/doc/bson_vector_float32_view_length.rst new file mode 100644 index 00000000000..8ac74234951 --- /dev/null +++ b/src/libbson/doc/bson_vector_float32_view_length.rst @@ -0,0 +1,33 @@ +:man_page: bson_vector_float32_view_length + +bson_vector_float32_view_length() +================================= + +Return the number of elements in a Vector referenced by a :symbol:`bson_vector_float32_view_t`. + +Synopsis +-------- + +.. code-block:: c + + size_t + bson_vector_float32_view_length (bson_vector_float32_view_t view); + +Parameters +---------- + +* ``view``: A valid :symbol:`bson_vector_float32_view_t`. + +Description +----------- + +An element count is calculated from the view's stored binary block length. + +Returns +------- + +The number of elements, as a ``size_t``. + +.. seealso:: + + | :symbol:`bson_vector_float32_const_view_length` diff --git a/src/libbson/doc/bson_vector_float32_view_read.rst b/src/libbson/doc/bson_vector_float32_view_read.rst new file mode 100644 index 00000000000..fe6db04148a --- /dev/null +++ b/src/libbson/doc/bson_vector_float32_view_read.rst @@ -0,0 +1,41 @@ +:man_page: bson_vector_float32_view_read + +bson_vector_float32_view_read() +=============================== + +Copy a contiguous block of elements from a :symbol:`bson_vector_float32_view_t` into a C array of ``float``. + +Synopsis +-------- + +.. code-block:: c + + bool + bson_vector_float32_view_read (bson_vector_float32_view_t view, + float *values_out, + size_t element_count, + size_t vector_offset_elements); + +Parameters +---------- + +* ``view``: A valid :symbol:`bson_vector_float32_view_t`. +* ``values_out``: Location where the ``float`` elements will be read to. +* ``element_count``: Number of elements to read. +* ``vector_offset_elements``: The vector index of the first element to read. + +Description +----------- + +Elements are copied in bulk from the view to the provided output pointer. + +Returns +------- + +If the ``element_count`` and ``vector_offset_elements`` parameters overflow the bounds of the Vector, returns false without taking any other action. +If the parameters are in range, this is guaranteed to succeed. +On success, returns true and reads ``element_count`` elements into ``*values_out``. + +.. seealso:: + + | :symbol:`bson_vector_float32_const_view_read` diff --git a/src/libbson/doc/bson_vector_float32_view_t.rst b/src/libbson/doc/bson_vector_float32_view_t.rst new file mode 100644 index 00000000000..bdd7fa5b217 --- /dev/null +++ b/src/libbson/doc/bson_vector_float32_view_t.rst @@ -0,0 +1,59 @@ +:man_page: bson_vector_float32_view_t + +bson_vector_float32_view_t +========================== + +A reference to mutable non-owned BSON Binary data holding a valid Vector of ``float32`` element type. + +Synopsis +-------- + +.. code-block:: c + + #include + + typedef struct bson_vector_float32_view_t { + /*< private >*/ + } bson_vector_float32_view_t; + +Description +----------- + +:symbol:`bson_vector_float32_view_t` is a structure that acts as an opaque reference to a block of memory that has been validated as a ``float32`` vector. + +It is meant to be passed by value and can be discarded at any time. The contents of the structure should be considered private. + +The :symbol:`bson_t` *MUST* be valid for the lifetime of the view and it is an error to modify the :symbol:`bson_t` while using the view. + +.. only:: html + + Functions + --------- + + .. toctree:: + :titlesonly: + :maxdepth: 1 + + bson_vector_float32_view_init + bson_vector_float32_view_from_iter + bson_vector_float32_view_as_const + bson_vector_float32_view_length + bson_vector_float32_view_read + bson_vector_float32_view_write + +Example +------- + +.. code-block:: c + + static const float values[] = {1.0f, 2.0f, 3.0f}; + const size_t values_count = sizeof values / sizeof values[0]; + + bson_vector_float32_view_t view; + BSON_ASSERT (BSON_APPEND_VECTOR_FLOAT32_UNINIT (&doc, "vector", values_count, &view)); + BSON_ASSERT (bson_vector_float32_view_write (view, values, values_count, 0)); + +.. seealso:: + + | :symbol:`bson_append_vector_float32_uninit` + | :symbol:`bson_vector_float32_const_view_t` diff --git a/src/libbson/doc/bson_vector_float32_view_write.rst b/src/libbson/doc/bson_vector_float32_view_write.rst new file mode 100644 index 00000000000..84788183bfb --- /dev/null +++ b/src/libbson/doc/bson_vector_float32_view_write.rst @@ -0,0 +1,37 @@ +:man_page: bson_vector_float32_view_write + +bson_vector_float32_view_write() +================================ + +Copy a contiguous block of elements from a C ``float`` array into a :symbol:`bson_vector_float32_view_t`. + +Synopsis +-------- + +.. code-block:: c + + bool + bson_vector_float32_view_write (bson_vector_float32_view_t view, + const float *values, + size_t element_count, + size_t vector_offset_elements) + +Parameters +---------- + +* ``view``: A valid :symbol:`bson_vector_float32_view_t`. +* ``values``: Location where the ``float`` elements will be copied from. +* ``element_count``: Number of elements to write. +* ``vector_offset_elements``: The vector index of the first element to write. + +Description +----------- + +Elements are copied in bulk from the provided pointer into the view. + +Returns +------- + +If the ``element_count`` and ``vector_offset_elements`` parameters overflow the bounds of the Vector, returns false without taking any other action. +If the parameters are in range, this is guaranteed to succeed. +On success, returns true and writes to ``element_count`` elements in the Vector starting at ``vector_offset_elements``. diff --git a/src/libbson/doc/bson_vector_int8_binary_data_length.rst b/src/libbson/doc/bson_vector_int8_binary_data_length.rst new file mode 100644 index 00000000000..cc4dbd4fb3f --- /dev/null +++ b/src/libbson/doc/bson_vector_int8_binary_data_length.rst @@ -0,0 +1,31 @@ +:man_page: bson_vector_int8_binary_data_length + +bson_vector_int8_binary_data_length() +===================================== + +Calculate the size of a BSON Binary field that would be needed to store a Vector with the indicated number of ``int8`` elements. + +Synopsis +-------- + +.. code-block:: c + + uint32_t + bson_vector_int8_binary_data_length (size_t element_count); + +Parameters +---------- + +* ``element_count``: Number of elements, as a ``size_t``. + +Description +----------- + +Checks ``element_count`` against the maximum representable size, and calculates the required Binary size. + +Returns +------- + +On success, returns the required Binary size as a ``uint32_t`` greater than or equal to ``BSON_VECTOR_HEADER_LEN``. +This length includes the 2-byte Vector header, but not the Binary subtype header or any other BSON headers. +If the ``element_count`` is too large to represent, returns 0. diff --git a/src/libbson/doc/bson_vector_int8_const_view_from_iter.rst b/src/libbson/doc/bson_vector_int8_const_view_from_iter.rst new file mode 100644 index 00000000000..abbe0bc6ce8 --- /dev/null +++ b/src/libbson/doc/bson_vector_int8_const_view_from_iter.rst @@ -0,0 +1,37 @@ +:man_page: bson_vector_int8_const_view_from_iter + +bson_vector_int8_const_view_from_iter() +======================================= + +Initialize a :symbol:`bson_vector_int8_const_view_t` from a :symbol:`bson_iter_t` pointing to a valid Vector of ``int8`` element type. + +Synopsis +-------- + +.. code-block:: c + + bool + bson_vector_int8_const_view_from_iter (bson_vector_int8_const_view_t *view_out, + const bson_iter_t *iter); + +Parameters +---------- + +* ``view_out``: A :symbol:`bson_vector_int8_const_view_t` is written here on success. +* ``iter``: A valid :symbol:`bson_iter_t`. + +Description +----------- + +The provided iterator, which must point to some kind of BSON item, will be checked for a valid Vector of ``int8`` element type. +On success, a :symbol:`bson_vector_int8_const_view_t` is set to point to the same underlying :symbol:`bson_t` buffer as the provided :symbol:`bson_iter_t`. +The view will only be valid until the containing document is destroyed or modified. + +Returns +------- + +Returns true if the view was successfully initialized. + +.. seealso:: + + | :symbol:`bson_vector_int8_view_from_iter` diff --git a/src/libbson/doc/bson_vector_int8_const_view_init.rst b/src/libbson/doc/bson_vector_int8_const_view_init.rst new file mode 100644 index 00000000000..d78fe69ebe8 --- /dev/null +++ b/src/libbson/doc/bson_vector_int8_const_view_init.rst @@ -0,0 +1,39 @@ +:man_page: bson_vector_int8_const_view_init + +bson_vector_int8_const_view_init() +================================== + +Initialize a :symbol:`bson_vector_int8_const_view_t` from a const ``uint8_t`` buffer. + +Synopsis +-------- + +.. code-block:: c + + bool + bson_vector_int8_const_view_init (bson_vector_int8_const_view_t *view_out, + const uint8_t *binary_data, + uint32_t binary_data_len); + +Parameters +---------- + +* ``view_out``: A :symbol:`bson_vector_int8_const_view_t` is written here on success. +* ``binary_data``: A pointer to the BSON Binary data block to be validated. +* ``binary_data_len``: Length of the binary data block, in bytes. + +Description +----------- + +The length and header of the provided binary data block will be checked for a valid Vector of ``int8`` element type. +On success, the pointer and length are packaged as a :symbol:`bson_vector_int8_const_view_t` written to ``*view_out``. +The view will only be valid as long as ``binary_data`` is valid. + +Returns +------- + +Returns true if the view was successfully initialized. + +.. seealso:: + + | :symbol:`bson_vector_int8_view_init` diff --git a/src/libbson/doc/bson_vector_int8_const_view_length.rst b/src/libbson/doc/bson_vector_int8_const_view_length.rst new file mode 100644 index 00000000000..4f584e57cb0 --- /dev/null +++ b/src/libbson/doc/bson_vector_int8_const_view_length.rst @@ -0,0 +1,33 @@ +:man_page: bson_vector_int8_const_view_length + +bson_vector_int8_const_view_length() +==================================== + +Return the number of elements in a Vector referenced by a :symbol:`bson_vector_int8_const_view_t`. + +Synopsis +-------- + +.. code-block:: c + + size_t + bson_vector_int8_const_view_length (bson_vector_int8_const_view_t view); + +Parameters +---------- + +* ``view``: A valid :symbol:`bson_vector_int8_const_view_t`. + +Description +----------- + +An element count is calculated from the view's stored binary block length. + +Returns +------- + +The number of elements, as a ``size_t``. + +.. seealso:: + + | :symbol:`bson_vector_int8_view_length` diff --git a/src/libbson/doc/bson_vector_int8_const_view_pointer.rst b/src/libbson/doc/bson_vector_int8_const_view_pointer.rst new file mode 100644 index 00000000000..555cfbd8d6a --- /dev/null +++ b/src/libbson/doc/bson_vector_int8_const_view_pointer.rst @@ -0,0 +1,36 @@ +:man_page: bson_vector_int8_const_view_pointer + +bson_vector_int8_const_view_pointer() +===================================== + +Obtain a direct ``int8_t`` pointer to the Vector elements referenced by a :symbol:`bson_vector_int8_const_view_t`. + +Synopsis +-------- + +.. code-block:: c + + const int8_t * + bson_vector_int8_const_view_pointer (bson_vector_int8_const_view_t view); + +Parameters +---------- + +* ``view``: A valid :symbol:`bson_vector_int8_const_view_t`. + +Description +----------- + +Unwraps a vector view into a bare pointer. +The ``int8`` Vector elements use a serialized format that's fully compatible with a C ``int8_t``. + +Returns +------- + +A pointer derived from the pointer this View was created from. +Its lifetime matches that of the original pointer. +If the view was created from a :symbol:`bson_iter_t`, it will be valid until the underlying :symbol:`bson_t` is otherwise modified or destroyed. + +.. seealso:: + + | :symbol:`bson_vector_int8_view_pointer` diff --git a/src/libbson/doc/bson_vector_int8_const_view_read.rst b/src/libbson/doc/bson_vector_int8_const_view_read.rst new file mode 100644 index 00000000000..8f66a1c0ffe --- /dev/null +++ b/src/libbson/doc/bson_vector_int8_const_view_read.rst @@ -0,0 +1,41 @@ +:man_page: bson_vector_int8_const_view_read + +bson_vector_int8_const_view_read() +================================== + +Copy a contiguous block of elements from a :symbol:`bson_vector_int8_const_view_t` into a C array of ``int8_t``. + +Synopsis +-------- + +.. code-block:: c + + bool + bson_vector_int8_const_view_read (bson_vector_int8_const_view_t view, + int8_t *values_out, + size_t element_count, + size_t vector_offset_elements); + +Parameters +---------- + +* ``view``: A valid :symbol:`bson_vector_int8_const_view_t`. +* ``values_out``: Location where the ``int8_t`` elements will be read to. +* ``element_count``: Number of elements to read. +* ``vector_offset_elements``: The vector index of the first element to read. + +Description +----------- + +Elements are copied in bulk from the view to the provided output pointer. + +Returns +------- + +If the ``element_count`` and ``vector_offset_elements`` parameters overflow the bounds of the Vector, returns false without taking any other action. +If the parameters are in range, this is guaranteed to succeed. +On success, returns true and reads ``element_count`` elements into ``*values_out``. + +.. seealso:: + + | :symbol:`bson_vector_int8_view_read` diff --git a/src/libbson/doc/bson_vector_int8_const_view_t.rst b/src/libbson/doc/bson_vector_int8_const_view_t.rst new file mode 100644 index 00000000000..aa9a97085aa --- /dev/null +++ b/src/libbson/doc/bson_vector_int8_const_view_t.rst @@ -0,0 +1,63 @@ +:man_page: bson_vector_int8_const_view_t + +bson_vector_int8_const_view_t +============================= + +A reference to non-owned const BSON Binary data holding a valid Vector of ``int8`` element type. + +Synopsis +-------- + +.. code-block:: c + + #include + + typedef struct bson_vector_int8_const_view_t { + /*< private >*/ + } bson_vector_int8_const_view_t; + +Description +----------- + +:symbol:`bson_vector_int8_const_view_t` is a structure that acts as an opaque const reference to a block of memory that has been validated as an ``int8`` vector. + +It is meant to be passed by value and can be discarded at any time. The contents of the structure should be considered private. + +The :symbol:`bson_t` *MUST* be valid for the lifetime of the view and it is an error to modify the :symbol:`bson_t` while using the view. + +.. only:: html + + Functions + --------- + + .. toctree:: + :titlesonly: + :maxdepth: 1 + + bson_vector_int8_const_view_init + bson_vector_int8_const_view_from_iter + bson_vector_int8_const_view_length + bson_vector_int8_const_view_read + bson_vector_int8_const_view_pointer + +Example +------- + +.. code-block:: c + + bson_iter_t iter; + bson_vector_int8_const_view_t view; + + if (bson_iter_init_find (&iter, &doc, "vector") && bson_vector_int8_const_view_from_iter (&view, &iter)) { + size_t length = bson_vector_int8_const_view_length (view); + printf ("Elements in 'vector':\n"); + for (size_t i = 0; i < length; i++) { + int8_t element; + BSON_ASSERT (bson_vector_int8_const_view_read (view, &element, 1, i)); + printf (" [%d] = %d\n", (int) i, (int) element); + } + } + +.. seealso:: + + | :symbol:`bson_vector_int8_view_t` diff --git a/src/libbson/doc/bson_vector_int8_view_as_const.rst b/src/libbson/doc/bson_vector_int8_view_as_const.rst new file mode 100644 index 00000000000..9c1704b7149 --- /dev/null +++ b/src/libbson/doc/bson_vector_int8_view_as_const.rst @@ -0,0 +1,29 @@ +:man_page: bson_vector_int8_view_as_const + +bson_vector_int8_view_as_const() +================================ + +Convert a :symbol:`bson_vector_int8_view_t` into a :symbol:`bson_vector_int8_const_view_t`. + +Synopsis +-------- + +.. code-block:: c + + bson_vector_int8_const_view_t + bson_vector_int8_view_as_const (bson_vector_int8_view_t view); + +Parameters +---------- + +* ``view``: A valid :symbol:`bson_vector_int8_view_t`. + +Description +----------- + +This adds a ``const`` qualifier to the view without re-validating the underlying data. + +Returns +------- + +Always returns a :symbol:`bson_vector_int8_const_view_t`. diff --git a/src/libbson/doc/bson_vector_int8_view_from_iter.rst b/src/libbson/doc/bson_vector_int8_view_from_iter.rst new file mode 100644 index 00000000000..0c9bbd80a2c --- /dev/null +++ b/src/libbson/doc/bson_vector_int8_view_from_iter.rst @@ -0,0 +1,37 @@ +:man_page: bson_vector_int8_view_from_iter + +bson_vector_int8_view_from_iter() +================================= + +Initialize a :symbol:`bson_vector_int8_view_t` from a :symbol:`bson_iter_t` pointing to a valid Vector of ``int8`` element type. + +Synopsis +-------- + +.. code-block:: c + + bool + bson_vector_int8_view_from_iter (bson_vector_int8_view_t *view_out, + bson_iter_t *iter); + +Parameters +---------- + +* ``view_out``: A :symbol:`bson_vector_int8_view_t` is written here on success. +* ``iter``: A valid :symbol:`bson_iter_t`. + +Description +----------- + +The provided iterator, which must point to some kind of BSON item, will be checked for a valid Vector of ``int8`` element type. +On success, a :symbol:`bson_vector_int8_view_t` is set to point to the same underlying :symbol:`bson_t` buffer as the provided :symbol:`bson_iter_t`. +The view will only be valid until the containing document is destroyed or otherwise modified. + +Returns +------- + +Returns true if the view was successfully initialized. + +.. seealso:: + + | :symbol:`bson_vector_int8_const_view_from_iter` diff --git a/src/libbson/doc/bson_vector_int8_view_init.rst b/src/libbson/doc/bson_vector_int8_view_init.rst new file mode 100644 index 00000000000..f53fff299fe --- /dev/null +++ b/src/libbson/doc/bson_vector_int8_view_init.rst @@ -0,0 +1,39 @@ +:man_page: bson_vector_int8_view_init + +bson_vector_int8_view_init() +============================ + +Initialize a :symbol:`bson_vector_int8_view_t` from a mutable ``uint8_t`` buffer. + +Synopsis +-------- + +.. code-block:: c + + bool + bson_vector_int8_view_init (bson_vector_int8_view_t *view_out, + uint8_t *binary_data, + uint32_t binary_data_len); + +Parameters +---------- + +* ``view_out``: A :symbol:`bson_vector_int8_view_t` is written here on success. +* ``binary_data``: A pointer to the BSON Binary data block to be validated. +* ``binary_data_len``: Length of the binary data block, in bytes. + +Description +----------- + +The length and header of the provided binary data block will be checked for a valid Vector of ``int8`` element type. +On success, the pointer and length are packaged as a :symbol:`bson_vector_int8_view_t` written to ``*view_out``. +The view will only be valid as long as ``binary_data`` is valid. + +Returns +------- + +Returns true if the view was successfully initialized. + +.. seealso:: + + | :symbol:`bson_vector_int8_const_view_init` diff --git a/src/libbson/doc/bson_vector_int8_view_length.rst b/src/libbson/doc/bson_vector_int8_view_length.rst new file mode 100644 index 00000000000..21dbf4cff57 --- /dev/null +++ b/src/libbson/doc/bson_vector_int8_view_length.rst @@ -0,0 +1,33 @@ +:man_page: bson_vector_int8_view_length + +bson_vector_int8_view_length() +============================== + +Return the number of elements in a Vector referenced by a :symbol:`bson_vector_int8_view_t`. + +Synopsis +-------- + +.. code-block:: c + + size_t + bson_vector_int8_view_length (bson_vector_int8_view_t view); + +Parameters +---------- + +* ``view``: A valid :symbol:`bson_vector_int8_view_t`. + +Description +----------- + +An element count is calculated from the view's stored binary block length. + +Returns +------- + +The number of elements, as a ``size_t``. + +.. seealso:: + + | :symbol:`bson_vector_int8_const_view_length` diff --git a/src/libbson/doc/bson_vector_int8_view_pointer.rst b/src/libbson/doc/bson_vector_int8_view_pointer.rst new file mode 100644 index 00000000000..afaa4630597 --- /dev/null +++ b/src/libbson/doc/bson_vector_int8_view_pointer.rst @@ -0,0 +1,36 @@ +:man_page: bson_vector_int8_view_pointer + +bson_vector_int8_view_pointer() +=============================== + +Obtain a direct ``int8_t`` pointer to the Vector elements referenced by a :symbol:`bson_vector_int8_view_t`. + +Synopsis +-------- + +.. code-block:: c + + int8_t * + bson_vector_int8_view_pointer (bson_vector_int8_view_t view); + +Parameters +---------- + +* ``view``: A valid :symbol:`bson_vector_int8_view_t`. + +Description +----------- + +Unwraps a vector view into a bare pointer. +The ``int8`` Vector elements use a serialized format that's fully compatible with a C ``int8_t``. + +Returns +------- + +A pointer derived from the pointer this View was created from. +Its lifetime matches that of the original pointer. +If the view was created from a :symbol:`bson_iter_t`, it will be valid until the underlying :symbol:`bson_t` is otherwise modified or destroyed. + +.. seealso:: + + | :symbol:`bson_vector_int8_const_view_pointer` diff --git a/src/libbson/doc/bson_vector_int8_view_read.rst b/src/libbson/doc/bson_vector_int8_view_read.rst new file mode 100644 index 00000000000..7f12d08b04c --- /dev/null +++ b/src/libbson/doc/bson_vector_int8_view_read.rst @@ -0,0 +1,41 @@ +:man_page: bson_vector_int8_view_read + +bson_vector_int8_view_read() +============================ + +Copy a contiguous block of elements from a :symbol:`bson_vector_int8_view_t` into a C array of ``int8_t``. + +Synopsis +-------- + +.. code-block:: c + + bool + bson_vector_int8_view_read (bson_vector_int8_view_t view, + int8_t *values_out, + size_t element_count, + size_t vector_offset_elements); + +Parameters +---------- + +* ``view``: A valid :symbol:`bson_vector_int8_view_t`. +* ``values_out``: Location where the ``int8_t`` elements will be read to. +* ``element_count``: Number of elements to read. +* ``vector_offset_elements``: The vector index of the first element to read. + +Description +----------- + +Elements are copied in bulk from the view to the provided output pointer. + +Returns +------- + +If the ``element_count`` and ``vector_offset_elements`` parameters overflow the bounds of the Vector, returns false without taking any other action. +If the parameters are in range, this is guaranteed to succeed. +On success, returns true and reads ``element_count`` elements into ``*values_out``. + +.. seealso:: + + | :symbol:`bson_vector_int8_const_view_read` diff --git a/src/libbson/doc/bson_vector_int8_view_t.rst b/src/libbson/doc/bson_vector_int8_view_t.rst new file mode 100644 index 00000000000..663b184203d --- /dev/null +++ b/src/libbson/doc/bson_vector_int8_view_t.rst @@ -0,0 +1,60 @@ +:man_page: bson_vector_int8_view_t + +bson_vector_int8_view_t +======================= + +A reference to mutable non-owned BSON Binary data holding a valid Vector of ``int8`` element type. + +Synopsis +-------- + +.. code-block:: c + + #include + + typedef struct bson_vector_int8_view_t { + /*< private >*/ + } bson_vector_int8_view_t; + +Description +----------- + +:symbol:`bson_vector_int8_view_t` is a structure that acts as an opaque reference to a block of memory that has been validated as an ``int8`` vector. + +It is meant to be passed by value and can be discarded at any time. The contents of the structure should be considered private. + +The :symbol:`bson_t` *MUST* be valid for the lifetime of the view and it is an error to modify the :symbol:`bson_t` while using the view. + +.. only:: html + + Functions + --------- + + .. toctree:: + :titlesonly: + :maxdepth: 1 + + bson_vector_int8_view_init + bson_vector_int8_view_from_iter + bson_vector_int8_view_as_const + bson_vector_int8_view_length + bson_vector_int8_view_read + bson_vector_int8_view_write + bson_vector_int8_view_pointer + +Example +------- + +.. code-block:: c + + static const int8_t values[] = {1, 2, 3}; + const size_t values_count = sizeof values / sizeof values[0]; + + bson_vector_int8_view_t view; + BSON_ASSERT (BSON_APPEND_VECTOR_INT8_UNINIT (&doc, "vector", values_count, &view)); + BSON_ASSERT (bson_vector_int8_view_write (view, values, values_count, 0)); + +.. seealso:: + + | :symbol:`bson_append_vector_int8_uninit` + | :symbol:`bson_vector_int8_const_view_t` diff --git a/src/libbson/doc/bson_vector_int8_view_write.rst b/src/libbson/doc/bson_vector_int8_view_write.rst new file mode 100644 index 00000000000..317c24f2b6c --- /dev/null +++ b/src/libbson/doc/bson_vector_int8_view_write.rst @@ -0,0 +1,37 @@ +:man_page: bson_vector_int8_view_write + +bson_vector_int8_view_write() +============================= + +Copy a contiguous block of elements from a C ``int8_t`` array into a :symbol:`bson_vector_int8_view_t`. + +Synopsis +-------- + +.. code-block:: c + + bool + bson_vector_int8_view_write (bson_vector_int8_view_t view, + const int8_t *values, + size_t element_count, + size_t vector_offset_elements); + +Parameters +---------- + +* ``view``: A valid :symbol:`bson_vector_int8_view_t`. +* ``values``: Location where the ``int8_t`` elements will be copied from. +* ``element_count``: Number of elements to write. +* ``vector_offset_elements``: The vector index of the first element to write. + +Description +----------- + +Elements are copied in bulk from the provided pointer into the view. + +Returns +------- + +If the ``element_count`` and ``vector_offset_elements`` parameters overflow the bounds of the Vector, returns false without taking any other action. +If the parameters are in range, this is guaranteed to succeed. +On success, returns true and writes to ``element_count`` elements in the Vector starting at ``vector_offset_elements``. diff --git a/src/libbson/doc/bson_vector_packed_bit_binary_data_length.rst b/src/libbson/doc/bson_vector_packed_bit_binary_data_length.rst new file mode 100644 index 00000000000..0d073db4360 --- /dev/null +++ b/src/libbson/doc/bson_vector_packed_bit_binary_data_length.rst @@ -0,0 +1,31 @@ +:man_page: bson_vector_packed_bit_binary_data_length + +bson_vector_packed_bit_binary_data_length() +=========================================== + +Calculate the size of a BSON Binary field that would be needed to store a Vector with the indicated number of ``packed_bit`` elements. + +Synopsis +-------- + +.. code-block:: c + + uint32_t + bson_vector_packed_bit_binary_data_length (size_t element_count); + +Parameters +---------- + +* ``element_count``: Number of single-bit elements, as a ``size_t``. + +Description +----------- + +Checks ``element_count`` against the maximum representable size, and calculates the required Binary size. + +Returns +------- + +On success, returns the required Binary size as a ``uint32_t`` greater than or equal to ``BSON_VECTOR_HEADER_LEN``. +This length includes the 2-byte Vector header, but not the Binary subtype header or any other BSON headers. +If the ``element_count`` is too large to represent, returns 0. diff --git a/src/libbson/doc/bson_vector_packed_bit_const_view_from_iter.rst b/src/libbson/doc/bson_vector_packed_bit_const_view_from_iter.rst new file mode 100644 index 00000000000..664438ec8b1 --- /dev/null +++ b/src/libbson/doc/bson_vector_packed_bit_const_view_from_iter.rst @@ -0,0 +1,37 @@ +:man_page: bson_vector_packed_bit_const_view_from_iter + +bson_vector_packed_bit_const_view_from_iter() +============================================= + +Initialize a :symbol:`bson_vector_packed_bit_const_view_t` from a :symbol:`bson_iter_t` pointing to a valid Vector of ``packed_bit`` element type. + +Synopsis +-------- + +.. code-block:: c + + bool + bson_vector_packed_bit_const_view_from_iter (bson_vector_packed_bit_const_view_t *view_out, + const bson_iter_t *iter); + +Parameters +---------- + +* ``view_out``: A :symbol:`bson_vector_packed_bit_const_view_t` is written here on success. +* ``iter``: A valid :symbol:`bson_iter_t`. + +Description +----------- + +The provided iterator, which must point to some kind of BSON item, will be checked for a valid Vector of ``packed_bit`` element type. +On success, a :symbol:`bson_vector_packed_bit_const_view_t` is set to point to the same underlying :symbol:`bson_t` buffer as the provided :symbol:`bson_iter_t`. +The view will only be valid until the containing document is destroyed or modified. + +Returns +------- + +Returns true if the view was successfully initialized. + +.. seealso:: + + | :symbol:`bson_vector_packed_bit_view_from_iter` diff --git a/src/libbson/doc/bson_vector_packed_bit_const_view_init.rst b/src/libbson/doc/bson_vector_packed_bit_const_view_init.rst new file mode 100644 index 00000000000..564283d3f95 --- /dev/null +++ b/src/libbson/doc/bson_vector_packed_bit_const_view_init.rst @@ -0,0 +1,39 @@ +:man_page: bson_vector_packed_bit_const_view_init + +bson_vector_packed_bit_const_view_init() +======================================== + +Initialize a :symbol:`bson_vector_packed_bit_const_view_t` from a const ``uint8_t`` buffer. + +Synopsis +-------- + +.. code-block:: c + + bool + bson_vector_packed_bit_const_view_init (bson_vector_packed_bit_const_view_t *view_out, + const uint8_t *binary_data, + uint32_t binary_data_len); + +Parameters +---------- + +* ``view_out``: A :symbol:`bson_vector_packed_bit_const_view_t` is written here on success. +* ``binary_data``: A pointer to the BSON Binary data block to be validated. +* ``binary_data_len``: Length of the binary data block, in bytes. + +Description +----------- + +The length, header, and trailing padding of the provided binary data block will be checked for a valid Vector of ``packed_bit`` element type. +On success, the pointer and length are packaged as a :symbol:`bson_vector_packed_bit_const_view_t` written to ``*view_out``. +The view will only be valid as long as ``binary_data`` is valid. + +Returns +------- + +Returns true if the view was successfully initialized. + +.. seealso:: + + | :symbol:`bson_vector_packed_bit_view_init` diff --git a/src/libbson/doc/bson_vector_packed_bit_const_view_length.rst b/src/libbson/doc/bson_vector_packed_bit_const_view_length.rst new file mode 100644 index 00000000000..b418614eb80 --- /dev/null +++ b/src/libbson/doc/bson_vector_packed_bit_const_view_length.rst @@ -0,0 +1,33 @@ +:man_page: bson_vector_packed_bit_const_view_length + +bson_vector_packed_bit_const_view_length() +========================================== + +Return the number of elements in a Vector referenced by a :symbol:`bson_vector_packed_bit_const_view_t`. + +Synopsis +-------- + +.. code-block:: c + + size_t + bson_vector_packed_bit_const_view_length (bson_vector_packed_bit_const_view_t view); + +Parameters +---------- + +* ``view``: A valid :symbol:`bson_vector_packed_bit_const_view_t`. + +Description +----------- + +An element count is calculated from information stored inside the `bson_vector_packed_bit_const_view_t` value. + +Returns +------- + +The number of elements, as a ``size_t``. + +.. seealso:: + + | :symbol:`bson_vector_packed_bit_view_length` diff --git a/src/libbson/doc/bson_vector_packed_bit_const_view_length_bytes.rst b/src/libbson/doc/bson_vector_packed_bit_const_view_length_bytes.rst new file mode 100644 index 00000000000..781beef6cc7 --- /dev/null +++ b/src/libbson/doc/bson_vector_packed_bit_const_view_length_bytes.rst @@ -0,0 +1,34 @@ +:man_page: bson_vector_packed_bit_const_view_length_bytes + +bson_vector_packed_bit_const_view_length_bytes() +================================================ + +Return the number of packed bytes in a Vector referenced by a :symbol:`bson_vector_packed_bit_const_view_t`. + +Synopsis +-------- + +.. code-block:: c + + size_t + bson_vector_packed_bit_const_view_length_bytes (bson_vector_packed_bit_const_view_t view); + +Parameters +---------- + +* ``view``: A valid :symbol:`bson_vector_packed_bit_const_view_t`. + +Description +----------- + +A byte count is calculated from the view's stored binary block length. +If the element count isn't a multiple of 8, the final byte will include bits that do not belong to any element. + +Returns +------- + +The number of bytes, as a ``size_t``. + +.. seealso:: + + | :symbol:`bson_vector_packed_bit_view_length_bytes` diff --git a/src/libbson/doc/bson_vector_packed_bit_const_view_padding.rst b/src/libbson/doc/bson_vector_packed_bit_const_view_padding.rst new file mode 100644 index 00000000000..8c34f5f0ab7 --- /dev/null +++ b/src/libbson/doc/bson_vector_packed_bit_const_view_padding.rst @@ -0,0 +1,34 @@ +:man_page: bson_vector_packed_bit_const_view_padding + +bson_vector_packed_bit_const_view_padding() +=========================================== + +Returns the number of unused bits in a Vector referenced by a :symbol:`bson_vector_packed_bit_const_view_t`. + +Synopsis +-------- + +.. code-block:: c + + size_t + bson_vector_packed_bit_const_view_padding (bson_vector_packed_bit_const_view_t view); + +Parameters +---------- + +* ``view``: A valid :symbol:`bson_vector_packed_bit_const_view_t`. + +Description +----------- + +The 3-bit ``padding`` field is extracted from a copy of the Vector header inside ``view``. + +Returns +------- + +The number of unused bits in the final packed byte. Guaranteed to be between 0 and 7 inclusive. +Vector validation guarantees that empty Vectors have a ``padding`` of 0. + +.. seealso:: + + | :symbol:`bson_vector_packed_bit_view_padding` diff --git a/src/libbson/doc/bson_vector_packed_bit_const_view_read_packed.rst b/src/libbson/doc/bson_vector_packed_bit_const_view_read_packed.rst new file mode 100644 index 00000000000..21ff00a3297 --- /dev/null +++ b/src/libbson/doc/bson_vector_packed_bit_const_view_read_packed.rst @@ -0,0 +1,45 @@ +:man_page: bson_vector_packed_bit_const_view_read_packed + +bson_vector_packed_bit_const_view_read_packed() +=============================================== + +Copy a contiguous block of packed bytes out of a :symbol:`bson_vector_packed_bit_const_view_t`. + +Synopsis +-------- + +.. code-block:: c + + bool + bson_vector_packed_bit_const_view_read_packed (bson_vector_packed_bit_const_view_t view, + uint8_t *packed_values_out, + size_t byte_count, + size_t vector_offset_bytes); + +Parameters +---------- + +* ``view``: A valid :symbol:`bson_vector_packed_bit_const_view_t`. +* ``packed_values_out``: Location where the packed bytes will be read to. +* ``byte_count``: Number of bytes to read. +* ``vector_offset_bytes``: The byte index of the first packed byte to read. + +Description +----------- + +Packed bytes are copied in bulk from the view to the provided output pointer. + +If the Vector's element count isn't a multiple of 8, its final byte will include bits that do not belong to any element. +Vector validation checks that these bits are zero. + +Returns +------- + +If the ``byte_count`` and ``vector_offset_bytes`` parameters overflow the bounds of the Vector, returns false without taking any other action. +If the parameters are in range, this is guaranteed to succeed. +On success, returns true and reads ``byte_count`` bytes into ``*packed_values_out``. + +.. seealso:: + + | :symbol:`bson_vector_packed_bit_view_read_packed` + | :symbol:`bson_vector_packed_bit_view_write_packed` diff --git a/src/libbson/doc/bson_vector_packed_bit_const_view_t.rst b/src/libbson/doc/bson_vector_packed_bit_const_view_t.rst new file mode 100644 index 00000000000..bcc9b50705e --- /dev/null +++ b/src/libbson/doc/bson_vector_packed_bit_const_view_t.rst @@ -0,0 +1,75 @@ +:man_page: bson_vector_packed_bit_const_view_t + +bson_vector_packed_bit_const_view_t +=================================== + +A reference to non-owned const BSON Binary data holding a valid Vector of ``packed_bit`` element type. + +Synopsis +-------- + +.. code-block:: c + + #include + + typedef struct bson_vector_packed_bit_const_view_t { + /*< private >*/ + } bson_vector_packed_bit_const_view_t; + +Description +----------- + +:symbol:`bson_vector_packed_bit_const_view_t` is a structure that acts as an opaque const reference to a block of memory that has been validated as a ``packed_bit`` vector. + +It is meant to be passed by value and can be discarded at any time. The contents of the structure should be considered private. + +The :symbol:`bson_t` *MUST* be valid for the lifetime of the view and it is an error to modify the :symbol:`bson_t` while using the view. + +.. only:: html + + Functions + --------- + + .. toctree:: + :titlesonly: + :maxdepth: 1 + + bson_vector_packed_bit_const_view_init + bson_vector_packed_bit_const_view_from_iter + bson_vector_packed_bit_const_view_length + bson_vector_packed_bit_const_view_length_bytes + bson_vector_packed_bit_const_view_padding + bson_vector_packed_bit_const_view_read_packed + bson_vector_packed_bit_const_view_unpack_bool + +Example +------- + +.. code-block:: c + + bson_iter_t iter; + bson_vector_packed_bit_const_view_t view; + + if (bson_iter_init_find (&iter, &doc, "vector") && bson_vector_packed_bit_const_view_from_iter (&view, &iter)) { + size_t length = bson_vector_packed_bit_const_view_length (view); + size_t length_bytes = bson_vector_packed_bit_const_view_length_bytes (view); + size_t padding = bson_vector_packed_bit_const_view_padding (view); + + printf ("Elements in 'vector':\n"); + for (size_t i = 0; i < length; i++) { + bool element; + BSON_ASSERT (bson_vector_packed_bit_const_view_unpack_bool (view, &element, 1, i)); + printf (" elements[%d] = %d\n", (int) i, (int) element); + } + + printf ("Bytes in 'vector': (%d bits unused)\n", (int) padding); + for (size_t i = 0; i < length_bytes; i++) { + uint8_t packed_byte; + BSON_ASSERT (bson_vector_packed_bit_const_view_read_packed (view, &packed_byte, 1, i)); + printf (" bytes[%d] = 0x%02x\n", (int) i, (unsigned) packed_byte); + } + } + +.. seealso:: + + | :symbol:`bson_vector_packed_bit_view_t` diff --git a/src/libbson/doc/bson_vector_packed_bit_const_view_unpack_bool.rst b/src/libbson/doc/bson_vector_packed_bit_const_view_unpack_bool.rst new file mode 100644 index 00000000000..c459e56e4a9 --- /dev/null +++ b/src/libbson/doc/bson_vector_packed_bit_const_view_unpack_bool.rst @@ -0,0 +1,41 @@ +:man_page: bson_vector_packed_bit_const_view_unpack_bool + +bson_vector_packed_bit_const_view_unpack_bool() +=============================================== + +Unpack a contiguous block of elements from a :symbol:`bson_vector_packed_bit_const_view_t` into a C array of ``bool``. + +Synopsis +-------- + +.. code-block:: c + + bool + bson_vector_packed_bit_const_view_unpack_bool (bson_vector_packed_bit_const_view_t view, + bool *unpacked_values_out, + size_t element_count, + size_t vector_offset_elements); + +Parameters +---------- + +* ``view``: A valid :symbol:`bson_vector_packed_bit_const_view_t`. +* ``unpacked_values_out``: Location where the ``bool`` elements will be unpacked to. +* ``element_count``: Number of elements to unpack. +* ``vector_offset_elements``: The vector index of the first element to unpack. + +Description +----------- + +Elements are unpacked from individual bits into a C array of ``bool``. + +Returns +------- + +If the ``element_count`` and ``vector_offset_elements`` parameters overflow the bounds of the Vector, returns false without taking any other action. +If the parameters are in range, this is guaranteed to succeed. +On success, returns true and unpacks ``element_count`` elements into ``*unpacked_values_out``. + +.. seealso:: + + | :symbol:`bson_vector_packed_bit_view_unpack_bool` diff --git a/src/libbson/doc/bson_vector_packed_bit_view_as_const.rst b/src/libbson/doc/bson_vector_packed_bit_view_as_const.rst new file mode 100644 index 00000000000..bd10d7ec633 --- /dev/null +++ b/src/libbson/doc/bson_vector_packed_bit_view_as_const.rst @@ -0,0 +1,29 @@ +:man_page: bson_vector_packed_bit_view_as_const + +bson_vector_packed_bit_view_as_const() +====================================== + +Convert a :symbol:`bson_vector_packed_bit_view_t` into a :symbol:`bson_vector_packed_bit_const_view_t`. + +Synopsis +-------- + +.. code-block:: c + + bson_vector_packed_bit_const_view_t + bson_vector_packed_bit_view_as_const (bson_vector_packed_bit_view_t view); + +Parameters +---------- + +* ``view``: A valid :symbol:`bson_vector_packed_bit_view_t`. + +Description +----------- + +This adds a ``const`` qualifier to the view without re-validating the underlying data. + +Returns +------- + +Always returns a :symbol:`bson_vector_packed_bit_const_view_t`. diff --git a/src/libbson/doc/bson_vector_packed_bit_view_from_iter.rst b/src/libbson/doc/bson_vector_packed_bit_view_from_iter.rst new file mode 100644 index 00000000000..05d40ad8e4e --- /dev/null +++ b/src/libbson/doc/bson_vector_packed_bit_view_from_iter.rst @@ -0,0 +1,33 @@ +:man_page: bson_vector_packed_bit_view_from_iter + +bson_vector_packed_bit_view_from_iter() +======================================= + +Initialize a :symbol:`bson_vector_packed_bit_view_t` from a :symbol:`bson_iter_t` pointing to a valid Vector of ``packed_bit`` element type. + +Synopsis +-------- + +.. code-block:: c + + bool + bson_vector_packed_bit_view_from_iter (bson_vector_packed_bit_view_t *view_out, + bson_iter_t *iter); + +Parameters +---------- + +* ``view_out``: A :symbol:`bson_vector_packed_bit_view_t` is written here on success. +* ``iter``: A valid :symbol:`bson_iter_t`. + +Description +----------- + +The provided iterator, which must point to some kind of BSON item, will be checked for a valid Vector of ``packed_bit`` element type. +On success, a :symbol:`bson_vector_packed_bit_view_t` is set to point to the same underlying :symbol:`bson_t` buffer as the provided :symbol:`bson_iter_t`. +The view will only be valid until the containing document is destroyed or otherwise modified. + +Returns +------- + +Returns true if the view was successfully initialized. diff --git a/src/libbson/doc/bson_vector_packed_bit_view_init.rst b/src/libbson/doc/bson_vector_packed_bit_view_init.rst new file mode 100644 index 00000000000..e3aaf5838e3 --- /dev/null +++ b/src/libbson/doc/bson_vector_packed_bit_view_init.rst @@ -0,0 +1,35 @@ +:man_page: bson_vector_packed_bit_view_init + +bson_vector_packed_bit_view_init() +================================== + +Initialize a :symbol:`bson_vector_packed_bit_view_t` from a mutable ``uint8_t`` buffer. + +Synopsis +-------- + +.. code-block:: c + + bool + bson_vector_packed_bit_view_init (bson_vector_packed_bit_view_t *view_out, + uint8_t *binary_data, + uint32_t binary_data_len); + +Parameters +---------- + +* ``view_out``: A :symbol:`bson_vector_packed_bit_view_t` is written here on success. +* ``binary_data``: A pointer to the BSON Binary data block to be validated. +* ``binary_data_len``: Length of the binary data block, in bytes. + +Description +----------- + +The length, header, and trailing padding of the provided binary data block will be checked for a valid Vector of ``packed_bit`` element type. +On success, the pointer and length are packaged as a :symbol:`bson_vector_packed_bit_view_t` written to ``*view_out``. +The view will only be valid as long as ``binary_data`` is valid. + +Returns +------- + +Returns true if the view was successfully initialized. diff --git a/src/libbson/doc/bson_vector_packed_bit_view_length.rst b/src/libbson/doc/bson_vector_packed_bit_view_length.rst new file mode 100644 index 00000000000..fde82ffde56 --- /dev/null +++ b/src/libbson/doc/bson_vector_packed_bit_view_length.rst @@ -0,0 +1,29 @@ +:man_page: bson_vector_packed_bit_view_length + +bson_vector_packed_bit_view_length() +==================================== + +Return the number of elements in a Vector referenced by a :symbol:`bson_vector_packed_bit_view_t`. + +Synopsis +-------- + +.. code-block:: c + + size_t + bson_vector_packed_bit_view_length (bson_vector_packed_bit_view_t view); + +Parameters +---------- + +* ``view``: A valid :symbol:`bson_vector_packed_bit_view_t`. + +Description +----------- + +An element count is calculated from information stored inside the `bson_vector_packed_bit_view_t` value. + +Returns +------- + +The number of elements, as a ``size_t``. diff --git a/src/libbson/doc/bson_vector_packed_bit_view_length_bytes.rst b/src/libbson/doc/bson_vector_packed_bit_view_length_bytes.rst new file mode 100644 index 00000000000..7659f2e90ee --- /dev/null +++ b/src/libbson/doc/bson_vector_packed_bit_view_length_bytes.rst @@ -0,0 +1,30 @@ +:man_page: bson_vector_packed_bit_view_length_bytes + +bson_vector_packed_bit_view_length_bytes() +========================================== + +Return the number of packed bytes in a Vector referenced by a :symbol:`bson_vector_packed_bit_view_t`. + +Synopsis +-------- + +.. code-block:: c + + size_t + bson_vector_packed_bit_view_length_bytes (bson_vector_packed_bit_view_t view); + +Parameters +---------- + +* ``view``: A valid :symbol:`bson_vector_packed_bit_view_t`. + +Description +----------- + +A byte count is calculated from the view's stored binary block length. +If the element count isn't a multiple of 8, the final byte will include bits that do not belong to any element. + +Returns +------- + +The number of bytes, as a ``size_t``. diff --git a/src/libbson/doc/bson_vector_packed_bit_view_pack_bool.rst b/src/libbson/doc/bson_vector_packed_bit_view_pack_bool.rst new file mode 100644 index 00000000000..4f94b65ab8e --- /dev/null +++ b/src/libbson/doc/bson_vector_packed_bit_view_pack_bool.rst @@ -0,0 +1,37 @@ +:man_page: bson_vector_packed_bit_view_pack_bool + +bson_vector_packed_bit_view_pack_bool() +======================================= + +Pack a contiguous block of elements from a C ``bool`` array into a :symbol:`bson_vector_packed_bit_view_t`. + +Synopsis +-------- + +.. code-block:: c + + bool + bson_vector_packed_bit_view_pack_bool (bson_vector_packed_bit_view_t view, + const bool *unpacked_values, + size_t element_count, + size_t vector_offset_elements) + +Parameters +---------- + +* ``view``: A valid :symbol:`bson_vector_packed_bit_view_t`. +* ``unpacked_values``: Location where the ``bool`` elements will be packed from. +* ``element_count``: Number of elements to pack. +* ``vector_offset_elements``: The vector index of the first element to pack. + +Description +----------- + +Elements are packed into individual Vector bits from a C ``bool`` array. + +Returns +------- + +If the ``element_count`` and ``vector_offset_elements`` parameters overflow the bounds of the Vector, returns false without taking any other action. +If the parameters are in range, this is guaranteed to succeed. +On success, returns true and writes to ``element_count`` elements in the Vector starting at ``vector_offset_elements``. diff --git a/src/libbson/doc/bson_vector_packed_bit_view_padding.rst b/src/libbson/doc/bson_vector_packed_bit_view_padding.rst new file mode 100644 index 00000000000..c240602ec83 --- /dev/null +++ b/src/libbson/doc/bson_vector_packed_bit_view_padding.rst @@ -0,0 +1,34 @@ +:man_page: bson_vector_packed_bit_view_padding + +bson_vector_packed_bit_view_padding() +===================================== + +Returns the number of unused bits in a Vector referenced by a :symbol:`bson_vector_packed_bit_view_t`. + +Synopsis +-------- + +.. code-block:: c + + size_t + bson_vector_packed_bit_view_padding (bson_vector_packed_bit_view_t view); + +Parameters +---------- + +* ``view``: A valid :symbol:`bson_vector_packed_bit_view_t`. + +Description +----------- + +The 3-bit ``padding`` field is extracted from a copy of the Vector header inside ``view``. + +Returns +------- + +The number of unused bits in the final packed byte. Guaranteed to be between 0 and 7 inclusive. +Vector validation guarantees that empty Vectors have a ``padding`` of 0. + +.. seealso:: + + | :symbol:`bson_vector_packed_bit_const_view_padding` diff --git a/src/libbson/doc/bson_vector_packed_bit_view_read_packed.rst b/src/libbson/doc/bson_vector_packed_bit_view_read_packed.rst new file mode 100644 index 00000000000..8bb9ea6b046 --- /dev/null +++ b/src/libbson/doc/bson_vector_packed_bit_view_read_packed.rst @@ -0,0 +1,45 @@ +:man_page: bson_vector_packed_bit_view_read_packed + +bson_vector_packed_bit_view_read_packed() +========================================= + +Copy a contiguous block of packed bytes out of a :symbol:`bson_vector_packed_bit_view_t`. + +Synopsis +-------- + +.. code-block:: c + + bool + bson_vector_packed_bit_view_read_packed (bson_vector_packed_bit_view_t view, + uint8_t *packed_values_out, + size_t byte_count, + size_t vector_offset_bytes); + +Parameters +---------- + +* ``view``: A valid :symbol:`bson_vector_packed_bit_view_t`. +* ``packed_values_out``: Location where the packed bytes will be read to. +* ``byte_count``: Number of bytes to read. +* ``vector_offset_bytes``: The byte index of the first packed byte to read. + +Description +----------- + +Packed bytes are copied in bulk from the view to the provided output pointer. + +If the Vector's element count isn't a multiple of 8, its final byte will include bits that do not belong to any element. +Vector validation checks that these bits are zero. + +Returns +------- + +If the ``byte_count`` and ``vector_offset_bytes`` parameters overflow the bounds of the Vector, returns false without taking any other action. +If the parameters are in range, this is guaranteed to succeed. +On success, returns true and reads ``byte_count`` bytes into ``*packed_values_out``. + +.. seealso:: + + | :symbol:`bson_vector_packed_bit_const_view_read_packed` + | :symbol:`bson_vector_packed_bit_view_write_packed` diff --git a/src/libbson/doc/bson_vector_packed_bit_view_t.rst b/src/libbson/doc/bson_vector_packed_bit_view_t.rst new file mode 100644 index 00000000000..0d747a5b63c --- /dev/null +++ b/src/libbson/doc/bson_vector_packed_bit_view_t.rst @@ -0,0 +1,85 @@ +:man_page: bson_vector_packed_bit_view_t + +bson_vector_packed_bit_view_t +============================= + +A reference to mutable non-owned BSON Binary data holding a valid Vector of ``packed_bit`` element type. + +Synopsis +-------- + +.. code-block:: c + + #include + + typedef struct bson_vector_packed_bit_view_t { + /*< private >*/ + } bson_vector_packed_bit_view_t; + +Description +----------- + +:symbol:`bson_vector_packed_bit_view_t` is a structure that acts as an opaque reference to a block of memory that has been validated as a ``packed_bit`` vector. + +It is meant to be passed by value and can be discarded at any time. The contents of the structure should be considered private. + +The :symbol:`bson_t` *MUST* be valid for the lifetime of the view and it is an error to modify the :symbol:`bson_t` while using the view. + +.. only:: html + + Functions + --------- + + .. toctree:: + :titlesonly: + :maxdepth: 1 + + bson_vector_packed_bit_view_init + bson_vector_packed_bit_view_from_iter + bson_vector_packed_bit_view_as_const + bson_vector_packed_bit_view_length + bson_vector_packed_bit_view_length_bytes + bson_vector_packed_bit_view_padding + bson_vector_packed_bit_view_read_packed + bson_vector_packed_bit_view_write_packed + bson_vector_packed_bit_view_unpack_bool + bson_vector_packed_bit_view_pack_bool + +Example +------- + +.. code-block:: c + + // Fill a new vector with individual boolean elements + { + static const bool bool_values[] = {true, false, true, true, false}; + const size_t bool_values_count = sizeof bool_values / sizeof bool_values[0]; + + bson_vector_packed_bit_view_t view; + BSON_ASSERT (BSON_APPEND_VECTOR_PACKED_BIT_UNINIT (&doc, "from_bool", bool_values_count, &view)); + BSON_ASSERT (bson_vector_packed_bit_view_pack_bool (view, bool_values, bool_values_count, 0)); + } + + // Fill another new vector with packed bytes + { + static const uint8_t packed_bytes[] = {0xb0}; + const size_t unused_bits_count = 3; + const size_t packed_values_count = sizeof packed_bytes * 8 - unused_bits_count; + + bson_vector_packed_bit_view_t view; + BSON_ASSERT (BSON_APPEND_VECTOR_PACKED_BIT_UNINIT (&doc, "from_packed", packed_values_count, &view)); + BSON_ASSERT (bson_vector_packed_bit_view_write_packed (view, packed_bytes, sizeof packed_bytes, 0)); + } + + // Compare both vectors. They match exactly. + { + bson_iter_t from_bool_iter, from_packed_iter; + BSON_ASSERT (bson_iter_init_find (&from_bool_iter, &doc, "from_bool")); + BSON_ASSERT (bson_iter_init_find (&from_packed_iter, &doc, "from_packed")); + BSON_ASSERT (bson_iter_binary_equal (&from_bool_iter, &from_packed_iter)); + } + +.. seealso:: + + | :symbol:`bson_append_vector_packed_bit_uninit` + | :symbol:`bson_vector_packed_bit_const_view_t` diff --git a/src/libbson/doc/bson_vector_packed_bit_view_unpack_bool.rst b/src/libbson/doc/bson_vector_packed_bit_view_unpack_bool.rst new file mode 100644 index 00000000000..62339ecd5ec --- /dev/null +++ b/src/libbson/doc/bson_vector_packed_bit_view_unpack_bool.rst @@ -0,0 +1,41 @@ +:man_page: bson_vector_packed_bit_view_unpack_bool + +bson_vector_packed_bit_view_unpack_bool() +========================================= + +Unpack a contiguous block of elements from a :symbol:`bson_vector_packed_bit_view_t` into a C array of ``bool``. + +Synopsis +-------- + +.. code-block:: c + + bool + bson_vector_packed_bit_view_unpack_bool (bson_vector_packed_bit_view_t view, + bool *unpacked_values_out, + size_t element_count, + size_t vector_offset_elements); + +Parameters +---------- + +* ``view``: A valid :symbol:`bson_vector_packed_bit_view_t`. +* ``unpacked_values_out``: Location where the ``bool`` elements will be unpacked to. +* ``element_count``: Number of elements to unpack. +* ``vector_offset_elements``: The vector index of the first element to unpack. + +Description +----------- + +Elements are unpacked from individual bits into a C array of ``bool``. + +Returns +------- + +If the ``element_count`` and ``vector_offset_elements`` parameters overflow the bounds of the Vector, returns false without taking any other action. +If the parameters are in range, this is guaranteed to succeed. +On success, returns true and unpacks ``element_count`` elements into ``*unpacked_values_out``. + +.. seealso:: + + | :symbol:`bson_vector_packed_bit_const_view_unpack_bool` diff --git a/src/libbson/doc/bson_vector_packed_bit_view_write_packed.rst b/src/libbson/doc/bson_vector_packed_bit_view_write_packed.rst new file mode 100644 index 00000000000..7b0f1174039 --- /dev/null +++ b/src/libbson/doc/bson_vector_packed_bit_view_write_packed.rst @@ -0,0 +1,45 @@ +:man_page: bson_vector_packed_bit_view_write_packed + +bson_vector_packed_bit_view_write_packed() +========================================== + +Copy a contiguous block of packed bytes into a :symbol:`bson_vector_packed_bit_view_t`. + +Synopsis +-------- + +.. code-block:: c + + bool + bson_vector_packed_bit_view_write_packed (bson_vector_packed_bit_view_t view, + const uint8_t *packed_values, + size_t byte_count, + size_t vector_offset_bytes); + +Parameters +---------- + +* ``view``: A valid :symbol:`bson_vector_packed_bit_view_t`. +* ``packed_values``: Location where the packed bytes will be copied from. +* ``byte_count``: Number of bytes to write. +* ``vector_offset_bytes``: The byte index of the first packed byte to write. + +Description +----------- + +Packed bytes are copied in bulk from the input pointer to the provided view. + +If the Vector's element count isn't a multiple of 8, its final byte will include bits that do not belong to any element. +This function cannot be used to modify the unused bits, they will be explicitly zeroed if set. + +Returns +------- + +If the ``byte_count`` and ``vector_offset_bytes`` parameters overflow the bounds of the Vector, returns false without taking any other action. +If the parameters are in range, this is guaranteed to succeed. +On success, returns true and writes ``byte_count`` bytes from ``*packed_values`` into the Vector starting at byte index ``vector_offset_bytes``. + +.. seealso:: + + | :symbol:`bson_vector_packed_bit_view_read_packed` + | :symbol:`bson_vector_packed_bit_const_view_read_packed` diff --git a/src/libbson/src/bson/bson-endian.h b/src/libbson/src/bson/bson-endian.h index 9b6cf9ef531..8ea44153275 100644 --- a/src/libbson/src/bson/bson-endian.h +++ b/src/libbson/src/bson/bson-endian.h @@ -83,6 +83,8 @@ BSON_BEGIN_DECLS #define BSON_UINT64_TO_BE(v) BSON_UINT64_SWAP_LE_BE (v) #define BSON_DOUBLE_FROM_LE(v) ((double) v) #define BSON_DOUBLE_TO_LE(v) ((double) v) +#define BSON_FLOAT_FROM_LE(v) ((float) v) +#define BSON_FLOAT_TO_LE(v) ((float) v) #elif BSON_BYTE_ORDER == BSON_BIG_ENDIAN #define BSON_UINT16_FROM_LE(v) BSON_UINT16_SWAP_LE_BE (v) #define BSON_UINT16_TO_LE(v) BSON_UINT16_SWAP_LE_BE (v) @@ -98,6 +100,8 @@ BSON_BEGIN_DECLS #define BSON_UINT64_TO_BE(v) ((uint64_t) v) #define BSON_DOUBLE_FROM_LE(v) (__bson_double_swap_slow (v)) #define BSON_DOUBLE_TO_LE(v) (__bson_double_swap_slow (v)) +#define BSON_FLOAT_FROM_LE(v) (__bson_float_swap_slow (v)) +#define BSON_FLOAT_TO_LE(v) (__bson_float_swap_slow (v)) #else #error "The endianness of target architecture is unknown." #endif @@ -205,6 +209,37 @@ __bson_double_swap_slow (double v) /* IN */ return v; } + +/* + *-------------------------------------------------------------------------- + * + * __bson_float_swap_slow -- + * + * Fallback endianness conversion for single floating point. + * + * Returns: + * The endian swapped version. + * + * Side effects: + * None. + * + *-------------------------------------------------------------------------- + */ + +BSON_STATIC_ASSERT2 (sizeof_uint32_t, sizeof (float) == sizeof (uint32_t)); + +static BSON_INLINE float +__bson_float_swap_slow (float v) /* IN */ +{ + uint32_t uv; + + memcpy (&uv, &v, sizeof (v)); + uv = BSON_UINT32_SWAP_LE_BE (uv); + memcpy (&v, &uv, sizeof (v)); + + return v; +} + BSON_END_DECLS diff --git a/src/libbson/src/bson/bson-error.h b/src/libbson/src/bson/bson-error.h index 855af02bb19..154800f2a18 100644 --- a/src/libbson/src/bson/bson-error.h +++ b/src/libbson/src/bson/bson-error.h @@ -32,6 +32,7 @@ BSON_BEGIN_DECLS #define BSON_ERROR_JSON 1 #define BSON_ERROR_READER 2 #define BSON_ERROR_INVALID 3 +#define BSON_ERROR_VECTOR 4 BSON_EXPORT (void) diff --git a/src/libbson/src/bson/bson-iter.c b/src/libbson/src/bson/bson-iter.c index b5bac45e901..bdc09755ac0 100644 --- a/src/libbson/src/bson/bson-iter.c +++ b/src/libbson/src/bson/bson-iter.c @@ -881,6 +881,12 @@ bson_iter_next (bson_iter_t *iter) /* INOUT */ * @binary should not be modified or freed and is only valid while * @iter's bson_t is valid and unmodified. * + * Note: Public constraints are tighter than private ones. + * API documentation says it's "a programming error to call this function + * when ``iter`` is not observing an element of type BSON_TYPE_BINARY.". + * Privately only, we do check the iterator type and we output NULL and + * BSON_SUBTYPE_BINARY when the type is incorrect. + * * Parameters: * @iter: A bson_iter_t * @subtype: A location for the binary subtype. @@ -942,6 +948,149 @@ bson_iter_binary (const bson_iter_t *iter, /* IN */ } +/* + *-------------------------------------------------------------------------- + * + * bson_iter_overwrite_binary -- + * + * Obtain temporary mutable access to the contents of a BSON_TYPE_BINARY + * field. It may be modified in content only, without changing length + * or subtype, through a temporary pointer that's only valid until the + * underlying bson_t is modified or deleted. + * + * Note: Public constraints are tighter than private ones. + * API documentation says it's "a programming error to call this function + * when ``iter`` is not observing an element of type BSON_TYPE_BINARY.". + * Privately only, we do check the iterator type and we return NULL when + * the type is incorrect. + * + * Parameters: + * @iter: A bson_iter_t + * @binary_len: A location for the length of @binary. + * @binary: A location for a pointer to the binary data. + * + * Returns: + * On success, returns a pointer in *binary and a length in *binary_len. + * The pointer is invalidated when the underlying bson_t is destroyed or modified. + * If the iter does not point to a binary item of the indicated subtype, + * returns NULL in *binary and 0 *binary_len. + * + * Side effects: + * None. + * + *-------------------------------------------------------------------------- + */ + +BSON_EXPORT (void) +bson_iter_overwrite_binary (bson_iter_t *iter, /* IN */ + bson_subtype_t subtype, /* IN */ + uint32_t *binary_len, /* OUT */ + uint8_t **binary) /* OUT */ +{ + BSON_ASSERT_PARAM (iter); + BSON_OPTIONAL_PARAM (binary_len); + BSON_OPTIONAL_PARAM (binary); + + bson_subtype_t iter_subtype; + uint32_t iter_binary_len; + const uint8_t *iter_binary; + bson_iter_binary (iter, &iter_subtype, &iter_binary_len, &iter_binary); + + if (iter_binary && iter_subtype == subtype) { + // All of bson_iter_overwrite_* work by casting away const from iter->raw. + if (binary) { + *binary = (void *) iter_binary; + } + if (binary_len) { + *binary_len = iter_binary_len; + } + return; + } + if (binary) { + *binary = NULL; + } + if (binary_len) { + *binary_len = 0; + } +} + + +/* + *-------------------------------------------------------------------------- + * + * bson_iter_binary_subtype -- + * + * Retrieves the subtype of a BSON_TYPE_BINARY field. + * + * Note: Public constraints are tighter than private ones. + * API documentation says it's "a programming error to call this function + * when ``iter`` is not observing an element of type BSON_TYPE_BINARY.". + * Privately only, we do check the iterator type and return + * BSON_SUBTYPE_BINARY when the type is incorrect. + * + * Parameters: + * @iter: A bson_iter_t + * + * Returns: + * Same as the @subtype OUT parameter from bson_iter_binary(). + * If the iterator is valid, returns the referenced subtype. Otherwise, + * returns BSON_SUBTYPE_BINARY as a fallback. + * + * Side effects: + * None. + * + *-------------------------------------------------------------------------- + */ + +BSON_EXPORT (bson_subtype_t) +bson_iter_binary_subtype (const bson_iter_t *iter) +{ + bson_subtype_t result; + bson_iter_binary (iter, &result, NULL, NULL); + return result; +} + + +/* + *-------------------------------------------------------------------------- + * + * bson_iter_binary_equal -- + * + * Compare two BSON_TYPE_BINARY fields for equality. + * + * Parameters: + * @iter_a: First bson_iter_t to compare + * @iter_b: Second bson_iter_t to compare + * + * Returns: + * true if both iterators point to BSON_TYPE_BINARY fields with + * identical subtype and contents. false if there is any difference. + * + * Side effects: + * None. + * + *-------------------------------------------------------------------------- + */ + +BSON_EXPORT (bool) +bson_iter_binary_equal (const bson_iter_t *iter_a, const bson_iter_t *iter_b) +{ + BSON_ASSERT_PARAM (iter_a); + BSON_ASSERT_PARAM (iter_b); + + if (BSON_ITER_HOLDS_BINARY (iter_a) && BSON_ITER_HOLDS_BINARY (iter_b)) { + bson_subtype_t subtypes[2]; + uint32_t lengths[2]; + const uint8_t *data[2]; + bson_iter_binary (iter_a, &subtypes[0], &lengths[0], &data[0]); + bson_iter_binary (iter_b, &subtypes[1], &lengths[1], &data[1]); + return subtypes[0] == subtypes[1] && lengths[0] == lengths[1] && 0 == memcmp (data[0], data[1], lengths[0]); + } else { + return false; + } +} + + /* *-------------------------------------------------------------------------- * diff --git a/src/libbson/src/bson/bson-iter.h b/src/libbson/src/bson/bson-iter.h index 5431902c260..65b74594979 100644 --- a/src/libbson/src/bson/bson-iter.h +++ b/src/libbson/src/bson/bson-iter.h @@ -40,6 +40,15 @@ BSON_BEGIN_DECLS #define BSON_ITER_HOLDS_BINARY(iter) (bson_iter_type ((iter)) == BSON_TYPE_BINARY) +#define BSON_ITER_HOLDS_VECTOR(iter) \ + (BSON_ITER_HOLDS_BINARY (iter) && bson_iter_binary_subtype (iter) == BSON_SUBTYPE_VECTOR) + +#define BSON_ITER_HOLDS_VECTOR_INT8(iter) (bson_vector_int8_const_view_from_iter (NULL, iter)) + +#define BSON_ITER_HOLDS_VECTOR_FLOAT32(iter) (bson_vector_float32_const_view_from_iter (NULL, iter)) + +#define BSON_ITER_HOLDS_VECTOR_PACKED_BIT(iter) (bson_vector_packed_bit_const_view_from_iter (NULL, iter)) + #define BSON_ITER_HOLDS_UNDEFINED(iter) (bson_iter_type ((iter)) == BSON_TYPE_UNDEFINED) #define BSON_ITER_HOLDS_OID(iter) (bson_iter_type ((iter)) == BSON_TYPE_OID) @@ -115,6 +124,15 @@ bson_iter_array (const bson_iter_t *iter, uint32_t *array_len, const uint8_t **a BSON_EXPORT (void) bson_iter_binary (const bson_iter_t *iter, bson_subtype_t *subtype, uint32_t *binary_len, const uint8_t **binary); +BSON_EXPORT (void) +bson_iter_overwrite_binary (bson_iter_t *iter, bson_subtype_t subtype, uint32_t *binary_len, uint8_t **binary); + +BSON_EXPORT (bson_subtype_t) +bson_iter_binary_subtype (const bson_iter_t *iter); + +BSON_EXPORT (bool) +bson_iter_binary_equal (const bson_iter_t *iter_a, const bson_iter_t *iter_b); + BSON_EXPORT (const char *) bson_iter_code (const bson_iter_t *iter, uint32_t *length); diff --git a/src/libbson/src/bson/bson-macros.h b/src/libbson/src/bson/bson-macros.h index ed92e047c03..ace5e161251 100644 --- a/src/libbson/src/bson/bson-macros.h +++ b/src/libbson/src/bson/bson-macros.h @@ -210,6 +210,18 @@ #endif +#if defined(__GNUC__) +#define BSON_RESTRICT __restrict__ +#elif defined(_MSC_VER) +#define BSON_RESTRICT __restrict +#elif !defined(__cplusplus) +// C99 (not C++) +#define BSON_RESTRICT restrict +#else +#define BSON_RESTRICT +#endif + + BSON_NORETURN static BSON_INLINE void _bson_assert_failed_on_line (const char *file, int line, const char *func, const char *test) { diff --git a/src/libbson/src/bson/bson-types.h b/src/libbson/src/bson/bson-types.h index 66407998a5e..4b1986f9bf7 100644 --- a/src/libbson/src/bson/bson-types.h +++ b/src/libbson/src/bson/bson-types.h @@ -274,6 +274,7 @@ typedef enum { BSON_SUBTYPE_ENCRYPTED = 0x06, BSON_SUBTYPE_COLUMN = 0x07, BSON_SUBTYPE_SENSITIVE = 0x08, + BSON_SUBTYPE_VECTOR = 0x09, BSON_SUBTYPE_USER = 0x80, } bson_subtype_t; diff --git a/src/libbson/src/bson/bson-vector-private.h b/src/libbson/src/bson/bson-vector-private.h new file mode 100644 index 00000000000..0882dead92d --- /dev/null +++ b/src/libbson/src/bson/bson-vector-private.h @@ -0,0 +1,60 @@ +/* + * Copyright 2009-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#ifndef BSON_VECTOR_PRIVATE_H +#define BSON_VECTOR_PRIVATE_H + +#include +#include + +BSON_BEGIN_DECLS + + +typedef enum { + BSON_VECTOR_ELEMENT_SIGNED_INT = 0, + BSON_VECTOR_ELEMENT_UNSIGNED_INT = 1, + BSON_VECTOR_ELEMENT_FLOAT = 2, +} bson_vector_element_type_t; + +typedef enum { + BSON_VECTOR_ELEMENT_1_BIT = 0, + BSON_VECTOR_ELEMENT_8_BITS = 3, + BSON_VECTOR_ELEMENT_32_BITS = 7, +} bson_vector_element_size_t; + + +static BSON_INLINE uint8_t +bson_vector_header_byte_0 (bson_vector_element_type_t element_type, bson_vector_element_size_t element_size) +{ + BSON_ASSERT ((unsigned) element_type <= 0x0f); + BSON_ASSERT ((unsigned) element_size <= 0x0f); + return (uint8_t) (((unsigned) element_type << 4) | (unsigned) element_size); +} + +// See also `bson_vector_padding_from_header_byte_1` defined in for use by public inline functions. +static BSON_INLINE uint8_t +bson_vector_header_byte_1 (size_t padding) +{ + BSON_ASSERT (padding <= 7); + return (uint8_t) padding; +} + + +BSON_END_DECLS + +#endif /* BSON_VECTOR_PRIVATE_H */ diff --git a/src/libbson/src/bson/bson-vector.c b/src/libbson/src/bson/bson-vector.c new file mode 100644 index 00000000000..dc3e8d61596 --- /dev/null +++ b/src/libbson/src/bson/bson-vector.c @@ -0,0 +1,681 @@ +/* + * Copyright 2009-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include + + +static BSON_INLINE bool +bson_vector_binary_header_impl_init (bson_vector_binary_header_impl_t *header_out, + const uint8_t *binary_data, + uint32_t binary_data_len) +{ + if (binary_data_len >= BSON_VECTOR_HEADER_LEN) { + memcpy (header_out->bytes, binary_data, BSON_VECTOR_HEADER_LEN); + return true; + } else { + return false; + } +} + +static BSON_INLINE bool +bson_vector_int8_validate (bson_vector_binary_header_impl_t header) +{ + return header.bytes[0] == bson_vector_header_byte_0 (BSON_VECTOR_ELEMENT_SIGNED_INT, BSON_VECTOR_ELEMENT_8_BITS) && + header.bytes[1] == bson_vector_header_byte_1 (0); +} + +static BSON_INLINE bool +bson_vector_float32_validate (bson_vector_binary_header_impl_t header, uint32_t binary_data_len) +{ + return (binary_data_len - BSON_VECTOR_HEADER_LEN) % sizeof (float) == 0 && + header.bytes[0] == bson_vector_header_byte_0 (BSON_VECTOR_ELEMENT_FLOAT, BSON_VECTOR_ELEMENT_32_BITS) && + header.bytes[1] == bson_vector_header_byte_1 (0); +} + +static BSON_INLINE bool +bson_vector_packed_bit_validate (bson_vector_binary_header_impl_t header, + const uint8_t *binary_data, + uint32_t binary_data_len) +{ + if (header.bytes[0] == bson_vector_header_byte_0 (BSON_VECTOR_ELEMENT_UNSIGNED_INT, BSON_VECTOR_ELEMENT_1_BIT)) { + size_t padding = bson_vector_padding_from_header_byte_1 (header.bytes[1]); + if (header.bytes[1] != bson_vector_header_byte_1 (padding)) { + return false; + } + uint32_t vector_data_len = binary_data_len - BSON_VECTOR_HEADER_LEN; + if (vector_data_len == 0) { + return padding == 0; + } else { + // We need to read the last byte of the binary block to validate that unused bits are zero. + uint8_t last_data_byte = binary_data[binary_data_len - 1]; + uint8_t mask_of_unused_bits = (uint8_t) ((1u << padding) - 1u); + return (last_data_byte & mask_of_unused_bits) == 0; + } + } else { + return false; + } +} + + +bool +bson_vector_int8_view_init (bson_vector_int8_view_t *view_out, uint8_t *binary_data, uint32_t binary_data_len) +{ + BSON_OPTIONAL_PARAM (view_out); + BSON_ASSERT_PARAM (binary_data); + bson_vector_binary_header_impl_t header; + if (bson_vector_binary_header_impl_init (&header, binary_data, binary_data_len) && + bson_vector_int8_validate (header)) { + if (view_out) { + *view_out = (bson_vector_int8_view_t){ + .binary.data = binary_data, .binary.data_len = binary_data_len, .binary.header_copy = header}; + } + return true; + } else { + return false; + } +} + +bool +bson_vector_int8_const_view_init (bson_vector_int8_const_view_t *view_out, + const uint8_t *binary_data, + uint32_t binary_data_len) +{ + BSON_OPTIONAL_PARAM (view_out); + BSON_ASSERT_PARAM (binary_data); + bson_vector_binary_header_impl_t header; + if (bson_vector_binary_header_impl_init (&header, binary_data, binary_data_len) && + bson_vector_int8_validate (header)) { + if (view_out) { + *view_out = (bson_vector_int8_const_view_t){ + .binary.data = binary_data, .binary.data_len = binary_data_len, .binary.header_copy = header}; + } + return true; + } else { + return false; + } +} + +bool +bson_vector_float32_view_init (bson_vector_float32_view_t *view_out, uint8_t *binary_data, uint32_t binary_data_len) +{ + BSON_OPTIONAL_PARAM (view_out); + BSON_ASSERT_PARAM (binary_data); + bson_vector_binary_header_impl_t header; + if (bson_vector_binary_header_impl_init (&header, binary_data, binary_data_len) && + bson_vector_float32_validate (header, binary_data_len)) { + if (view_out) { + *view_out = (bson_vector_float32_view_t){ + .binary.data = binary_data, .binary.data_len = binary_data_len, .binary.header_copy = header}; + } + return true; + } else { + return false; + } +} + +bool +bson_vector_float32_const_view_init (bson_vector_float32_const_view_t *view_out, + const uint8_t *binary_data, + uint32_t binary_data_len) +{ + BSON_OPTIONAL_PARAM (view_out); + BSON_ASSERT_PARAM (binary_data); + bson_vector_binary_header_impl_t header; + if (bson_vector_binary_header_impl_init (&header, binary_data, binary_data_len) && + bson_vector_float32_validate (header, binary_data_len)) { + if (view_out) { + *view_out = (bson_vector_float32_const_view_t){ + .binary.data = binary_data, .binary.data_len = binary_data_len, .binary.header_copy = header}; + } + return true; + } else { + return false; + } +} + +bool +bson_vector_packed_bit_view_init (bson_vector_packed_bit_view_t *view_out, + uint8_t *binary_data, + uint32_t binary_data_len) +{ + BSON_OPTIONAL_PARAM (view_out); + BSON_ASSERT_PARAM (binary_data); + bson_vector_binary_header_impl_t header; + if (bson_vector_binary_header_impl_init (&header, binary_data, binary_data_len) && + bson_vector_packed_bit_validate (header, binary_data, binary_data_len)) { + if (view_out) { + *view_out = (bson_vector_packed_bit_view_t){ + .binary.data = binary_data, .binary.data_len = binary_data_len, .binary.header_copy = header}; + } + return true; + } else { + return false; + } +} + +bool +bson_vector_packed_bit_const_view_init (bson_vector_packed_bit_const_view_t *view_out, + const uint8_t *binary_data, + uint32_t binary_data_len) +{ + BSON_OPTIONAL_PARAM (view_out); + BSON_ASSERT_PARAM (binary_data); + bson_vector_binary_header_impl_t header; + if (bson_vector_binary_header_impl_init (&header, binary_data, binary_data_len) && + bson_vector_packed_bit_validate (header, binary_data, binary_data_len)) { + if (view_out) { + *view_out = (bson_vector_packed_bit_const_view_t){ + .binary.data = binary_data, .binary.data_len = binary_data_len, .binary.header_copy = header}; + } + return true; + } else { + return false; + } +} + + +bool +bson_vector_int8_view_from_iter (bson_vector_int8_view_t *view_out, bson_iter_t *iter) +{ + BSON_OPTIONAL_PARAM (view_out); + BSON_ASSERT_PARAM (iter); + if (BSON_ITER_HOLDS_BINARY (iter)) { + uint32_t binary_len; + uint8_t *binary; + bson_iter_overwrite_binary (iter, BSON_SUBTYPE_VECTOR, &binary_len, &binary); + return binary && bson_vector_int8_view_init (view_out, binary, binary_len); + } else { + return false; + } +} + +bool +bson_vector_int8_const_view_from_iter (bson_vector_int8_const_view_t *view_out, const bson_iter_t *iter) +{ + BSON_OPTIONAL_PARAM (view_out); + BSON_ASSERT_PARAM (iter); + if (BSON_ITER_HOLDS_BINARY (iter)) { + bson_subtype_t subtype; + uint32_t binary_len; + const uint8_t *binary; + bson_iter_binary (iter, &subtype, &binary_len, &binary); + return binary && subtype == BSON_SUBTYPE_VECTOR && + bson_vector_int8_const_view_init (view_out, binary, binary_len); + } else { + return false; + } +} + +bool +bson_vector_float32_view_from_iter (bson_vector_float32_view_t *view_out, bson_iter_t *iter) +{ + BSON_OPTIONAL_PARAM (view_out); + BSON_ASSERT_PARAM (iter); + if (BSON_ITER_HOLDS_BINARY (iter)) { + uint32_t binary_len; + uint8_t *binary; + bson_iter_overwrite_binary (iter, BSON_SUBTYPE_VECTOR, &binary_len, &binary); + return binary && bson_vector_float32_view_init (view_out, binary, binary_len); + } else { + return false; + } +} + +bool +bson_vector_float32_const_view_from_iter (bson_vector_float32_const_view_t *view_out, const bson_iter_t *iter) +{ + BSON_OPTIONAL_PARAM (view_out); + BSON_ASSERT_PARAM (iter); + if (BSON_ITER_HOLDS_BINARY (iter)) { + bson_subtype_t subtype; + uint32_t binary_len; + const uint8_t *binary; + bson_iter_binary (iter, &subtype, &binary_len, &binary); + return binary && subtype == BSON_SUBTYPE_VECTOR && + bson_vector_float32_const_view_init (view_out, binary, binary_len); + } else { + return false; + } +} + +bool +bson_vector_packed_bit_view_from_iter (bson_vector_packed_bit_view_t *view_out, bson_iter_t *iter) +{ + BSON_OPTIONAL_PARAM (view_out); + BSON_ASSERT_PARAM (iter); + if (BSON_ITER_HOLDS_BINARY (iter)) { + uint32_t binary_len; + uint8_t *binary; + bson_iter_overwrite_binary (iter, BSON_SUBTYPE_VECTOR, &binary_len, &binary); + return binary && bson_vector_packed_bit_view_init (view_out, binary, binary_len); + } else { + return false; + } +} + +bool +bson_vector_packed_bit_const_view_from_iter (bson_vector_packed_bit_const_view_t *view_out, const bson_iter_t *iter) +{ + BSON_OPTIONAL_PARAM (view_out); + BSON_ASSERT_PARAM (iter); + if (BSON_ITER_HOLDS_BINARY (iter)) { + bson_subtype_t subtype; + uint32_t binary_len; + const uint8_t *binary; + bson_iter_binary (iter, &subtype, &binary_len, &binary); + return binary && subtype == BSON_SUBTYPE_VECTOR && + bson_vector_packed_bit_const_view_init (view_out, binary, binary_len); + } else { + return false; + } +} + + +bool +bson_append_vector_int8_uninit ( + bson_t *bson, const char *key, int key_length, size_t element_count, bson_vector_int8_view_t *view_out) +{ + BSON_ASSERT_PARAM (bson); + BSON_ASSERT_PARAM (view_out); + + uint32_t length = bson_vector_int8_binary_data_length (element_count); + if (length < BSON_VECTOR_HEADER_LEN) { + return false; + } + uint8_t *binary; + if (bson_append_binary_uninit (bson, key, key_length, BSON_SUBTYPE_VECTOR, &binary, length)) { + bson_vector_binary_header_impl_t header = { + .bytes[0] = bson_vector_header_byte_0 (BSON_VECTOR_ELEMENT_SIGNED_INT, BSON_VECTOR_ELEMENT_8_BITS), + .bytes[1] = bson_vector_header_byte_1 (0)}; + memcpy (binary, header.bytes, BSON_VECTOR_HEADER_LEN); + *view_out = + (bson_vector_int8_view_t){.binary.data = binary, .binary.data_len = length, .binary.header_copy = header}; + return true; + } else { + return false; + } +} + +bool +bson_append_vector_float32_uninit ( + bson_t *bson, const char *key, int key_length, size_t element_count, bson_vector_float32_view_t *view_out) +{ + BSON_ASSERT_PARAM (bson); + BSON_ASSERT_PARAM (view_out); + + uint32_t length = bson_vector_float32_binary_data_length (element_count); + if (length < BSON_VECTOR_HEADER_LEN) { + return false; + } + uint8_t *binary; + if (bson_append_binary_uninit (bson, key, key_length, BSON_SUBTYPE_VECTOR, &binary, length)) { + bson_vector_binary_header_impl_t header = { + .bytes[0] = bson_vector_header_byte_0 (BSON_VECTOR_ELEMENT_FLOAT, BSON_VECTOR_ELEMENT_32_BITS), + .bytes[1] = bson_vector_header_byte_1 (0)}; + memcpy (binary, header.bytes, BSON_VECTOR_HEADER_LEN); + *view_out = + (bson_vector_float32_view_t){.binary.data = binary, .binary.data_len = length, .binary.header_copy = header}; + return true; + } else { + return false; + } +} + +bool +bson_append_vector_packed_bit_uninit ( + bson_t *bson, const char *key, int key_length, size_t element_count, bson_vector_packed_bit_view_t *view_out) +{ + BSON_ASSERT_PARAM (bson); + BSON_ASSERT_PARAM (view_out); + + uint32_t length = bson_vector_packed_bit_binary_data_length (element_count); + if (length < BSON_VECTOR_HEADER_LEN) { + return false; + } + uint8_t *binary; + if (bson_append_binary_uninit (bson, key, key_length, BSON_SUBTYPE_VECTOR, &binary, length)) { + size_t padding = (size_t) 7 & -element_count; + bson_vector_binary_header_impl_t header = { + .bytes[0] = bson_vector_header_byte_0 (BSON_VECTOR_ELEMENT_UNSIGNED_INT, BSON_VECTOR_ELEMENT_1_BIT), + .bytes[1] = bson_vector_header_byte_1 (padding)}; + memcpy (binary, header.bytes, BSON_VECTOR_HEADER_LEN); + if (element_count > 0 && padding > 0) { + // We must explicitly zero bits in the final byte that aren't part of any element. + // No reason to read-modify-write here, it's better to write the whole byte. + binary[length - 1u] = 0u; + } + *view_out = (bson_vector_packed_bit_view_t){ + .binary.data = binary, .binary.data_len = length, .binary.header_copy = header}; + return true; + } else { + return false; + } +} + +static bool +bson_vector_from_array_expect_key (const bson_iter_t *iter, uint32_t numeric_key, bson_error_t *error) +{ + char buffer[16]; + const char *key; + bson_uint32_to_string (numeric_key, &key, buffer, sizeof buffer); + if (0 == strcmp (key, bson_iter_key (iter))) { + return true; + } else { + bson_set_error (error, + BSON_ERROR_VECTOR, + BSON_VECTOR_ERROR_ARRAY_KEY, + "expected BSON array key '%s', found key '%s'", + key, + bson_iter_key (iter)); + return false; + } +} + +static void +bson_vector_set_error_max_size (bson_error_t *error) +{ + bson_set_error ( + error, BSON_ERROR_VECTOR, BSON_VECTOR_ERROR_MAX_SIZE, "maximum BSON document size would be exceeded"); +} + +bool +bson_append_vector_int8_from_array ( + bson_t *bson, const char *key, int key_length, const bson_iter_t *iter, bson_error_t *error) +{ + BSON_ASSERT_PARAM (bson); + BSON_ASSERT_PARAM (key); + BSON_ASSERT_PARAM (iter); + + uint32_t element_count = 0; + { + bson_iter_t validation_iter = *iter; + while (bson_iter_next (&validation_iter)) { + if (!bson_vector_from_array_expect_key (&validation_iter, element_count, error)) { + return false; + } + if (!BSON_ITER_HOLDS_INT (&validation_iter)) { + bson_set_error (error, + BSON_ERROR_VECTOR, + BSON_VECTOR_ERROR_ARRAY_ELEMENT_TYPE, + "expected int32 or int64 in BSON array key '%s', found item type 0x%02X", + bson_iter_key (&validation_iter), + (unsigned) bson_iter_type (&validation_iter)); + return false; + } + int64_t element_as_int64 = bson_iter_as_int64 (&validation_iter); + if (element_as_int64 < INT8_MIN || element_as_int64 > INT8_MAX) { + bson_set_error (error, + BSON_ERROR_VECTOR, + BSON_VECTOR_ERROR_ARRAY_ELEMENT_VALUE, + "BSON array key '%s' value %" PRId64 " is out of range for vector of int8", + bson_iter_key (&validation_iter), + element_as_int64); + return false; + } + element_count++; + } + } + + bson_vector_int8_view_t view; + if (!bson_append_vector_int8_uninit (bson, key, key_length, element_count, &view)) { + bson_vector_set_error_max_size (error); + return false; + } + bson_iter_t copy_iter = *iter; + for (uint32_t i = 0; i < element_count; i++) { + BSON_ASSERT (bson_iter_next (©_iter)); + int8_t element = (int8_t) bson_iter_as_int64 (©_iter); + BSON_ASSERT (bson_vector_int8_view_write (view, &element, 1, i)); + } + return true; +} + +bool +bson_append_vector_float32_from_array ( + bson_t *bson, const char *key, int key_length, const bson_iter_t *iter, bson_error_t *error) +{ + BSON_ASSERT_PARAM (bson); + BSON_ASSERT_PARAM (key); + BSON_ASSERT_PARAM (iter); + + uint32_t element_count = 0; + { + bson_iter_t validation_iter = *iter; + while (bson_iter_next (&validation_iter)) { + if (!bson_vector_from_array_expect_key (&validation_iter, element_count, error)) { + return false; + } + if (!BSON_ITER_HOLDS_DOUBLE (&validation_iter)) { + bson_set_error (error, + BSON_ERROR_VECTOR, + BSON_VECTOR_ERROR_ARRAY_ELEMENT_TYPE, + "expected 'double' number type in BSON array key '%s', found item type 0x%02X", + bson_iter_key (&validation_iter), + (unsigned) bson_iter_type (&validation_iter)); + return false; + } + element_count++; + } + } + + bson_vector_float32_view_t view; + if (!bson_append_vector_float32_uninit (bson, key, key_length, element_count, &view)) { + bson_vector_set_error_max_size (error); + return false; + } + bson_iter_t copy_iter = *iter; + for (uint32_t i = 0; i < element_count; i++) { + BSON_ASSERT (bson_iter_next (©_iter)); + float element = (float) bson_iter_double (©_iter); + BSON_ASSERT (bson_vector_float32_view_write (view, &element, 1, i)); + } + return true; +} + +bool +bson_append_vector_packed_bit_from_array ( + bson_t *bson, const char *key, int key_length, const bson_iter_t *iter, bson_error_t *error) +{ + BSON_ASSERT_PARAM (bson); + BSON_ASSERT_PARAM (key); + BSON_ASSERT_PARAM (iter); + + uint32_t element_count = 0; + { + bson_iter_t validation_iter = *iter; + while (bson_iter_next (&validation_iter)) { + if (!bson_vector_from_array_expect_key (&validation_iter, element_count, error)) { + return false; + } + if (!BSON_ITER_HOLDS_INT (&validation_iter) && !BSON_ITER_HOLDS_BOOL (&validation_iter)) { + bson_set_error (error, + BSON_ERROR_VECTOR, + BSON_VECTOR_ERROR_ARRAY_ELEMENT_TYPE, + "expected int32, int64, or bool in BSON array key '%s', found item type 0x%02X", + bson_iter_key (&validation_iter), + (unsigned) bson_iter_type (&validation_iter)); + return false; + } + int64_t element_as_int64 = bson_iter_as_int64 (&validation_iter); + if (element_as_int64 < 0 || element_as_int64 > 1) { + bson_set_error (error, + BSON_ERROR_VECTOR, + BSON_VECTOR_ERROR_ARRAY_ELEMENT_VALUE, + "BSON array key '%s' value %" PRId64 " is out of range for vector of packed_bit", + bson_iter_key (&validation_iter), + element_as_int64); + return false; + } + element_count++; + } + } + + bson_vector_packed_bit_view_t view; + if (!bson_append_vector_packed_bit_uninit (bson, key, key_length, element_count, &view)) { + bson_vector_set_error_max_size (error); + return false; + } + bson_iter_t copy_iter = *iter; + for (uint32_t i = 0; i < element_count; i++) { + BSON_ASSERT (bson_iter_next (©_iter)); + bool element_as_bool = (bool) bson_iter_as_int64 (©_iter); + BSON_ASSERT (bson_vector_packed_bit_view_pack_bool (view, &element_as_bool, 1, i)); + } + return true; +} + + +bool +bson_array_builder_append_vector_int8_elements (bson_array_builder_t *builder, bson_vector_int8_const_view_t view) +{ + BSON_ASSERT_PARAM (builder); + size_t length = bson_vector_int8_const_view_length (view); + for (size_t i = 0; i < length; i++) { + // Note, the zero initializer is only needed due to a false positive -Wmaybe-uninitialized warning in uncommon + // configurations where the compiler does not have visibility into memcpy(). + int8_t element = 0; + BSON_ASSERT (bson_vector_int8_const_view_read (view, &element, 1, i)); + if (!bson_array_builder_append_int32 (builder, (int32_t) element)) { + return false; + } + } + return true; +} + +bool +bson_array_builder_append_vector_float32_elements (bson_array_builder_t *builder, bson_vector_float32_const_view_t view) +{ + BSON_ASSERT_PARAM (builder); + size_t length = bson_vector_float32_const_view_length (view); + for (size_t i = 0; i < length; i++) { + float element; + BSON_ASSERT (bson_vector_float32_const_view_read (view, &element, 1, i)); + if (!bson_array_builder_append_double (builder, (double) element)) { + return false; + } + } + return true; +} + +bool +bson_array_builder_append_vector_packed_bit_elements (bson_array_builder_t *builder, + bson_vector_packed_bit_const_view_t view) +{ + BSON_ASSERT_PARAM (builder); + size_t length = bson_vector_packed_bit_const_view_length (view); + for (size_t i = 0; i < length; i++) { + bool element; + BSON_ASSERT (bson_vector_packed_bit_const_view_unpack_bool (view, &element, 1, i)); + if (!bson_array_builder_append_int32 (builder, element ? 1 : 0)) { + return false; + } + } + return true; +} + + +bool +bson_array_builder_append_vector_elements (bson_array_builder_t *builder, const bson_iter_t *iter) +{ + BSON_ASSERT_PARAM (builder); + BSON_ASSERT_PARAM (iter); + { + bson_vector_int8_const_view_t view; + if (bson_vector_int8_const_view_from_iter (&view, iter)) { + return bson_array_builder_append_vector_int8_elements (builder, view); + } + } + { + bson_vector_float32_const_view_t view; + if (bson_vector_float32_const_view_from_iter (&view, iter)) { + return bson_array_builder_append_vector_float32_elements (builder, view); + } + } + { + bson_vector_packed_bit_const_view_t view; + if (bson_vector_packed_bit_const_view_from_iter (&view, iter)) { + return bson_array_builder_append_vector_packed_bit_elements (builder, view); + } + } + return false; +} + + +bool +bson_append_array_from_vector_int8 (bson_t *bson, const char *key, int key_length, bson_vector_int8_const_view_t view) +{ + BSON_ASSERT_PARAM (bson); + BSON_ASSERT_PARAM (key); + bson_array_builder_t *child; + if (bson_append_array_builder_begin (bson, key, key_length, &child)) { + bool ok = bson_array_builder_append_vector_int8_elements (child, view); + return bson_append_array_builder_end (bson, child) && ok; + } else { + return false; + } +} + +bool +bson_append_array_from_vector_float32 (bson_t *bson, + const char *key, + int key_length, + bson_vector_float32_const_view_t view) +{ + BSON_ASSERT_PARAM (bson); + BSON_ASSERT_PARAM (key); + bson_array_builder_t *child; + if (bson_append_array_builder_begin (bson, key, key_length, &child)) { + bool ok = bson_array_builder_append_vector_float32_elements (child, view); + return bson_append_array_builder_end (bson, child) && ok; + } else { + return false; + } +} + +bool +bson_append_array_from_vector_packed_bit (bson_t *bson, + const char *key, + int key_length, + bson_vector_packed_bit_const_view_t view) +{ + BSON_ASSERT_PARAM (bson); + BSON_ASSERT_PARAM (key); + bson_array_builder_t *child; + if (bson_append_array_builder_begin (bson, key, key_length, &child)) { + bool ok = bson_array_builder_append_vector_packed_bit_elements (child, view); + return bson_append_array_builder_end (bson, child) && ok; + } else { + return false; + } +} + + +bool +bson_append_array_from_vector (bson_t *bson, const char *key, int key_length, const bson_iter_t *iter) +{ + BSON_ASSERT_PARAM (bson); + BSON_ASSERT_PARAM (key); + BSON_ASSERT_PARAM (iter); + bson_array_builder_t *child; + if (bson_append_array_builder_begin (bson, key, key_length, &child)) { + bool ok = bson_array_builder_append_vector_elements (child, iter); + return bson_append_array_builder_end (bson, child) && ok; + } else { + return false; + } +} diff --git a/src/libbson/src/bson/bson-vector.h b/src/libbson/src/bson/bson-vector.h new file mode 100644 index 00000000000..69b90308bcb --- /dev/null +++ b/src/libbson/src/bson/bson-vector.h @@ -0,0 +1,613 @@ +/* + * Copyright 2009-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#ifndef BSON_VECTOR_H +#define BSON_VECTOR_H + +#include +#include +#include + +BSON_BEGIN_DECLS + + +// Length of the required header for BSON_SUBTYPE_VECTOR, in bytes +#define BSON_VECTOR_HEADER_LEN 2 + +// Forward declaration (typedef bson_array_builder_t in bson.h) +struct _bson_array_builder_t; + +/** @brief Error codes for domain BSON_ERROR_VECTOR */ +typedef enum { + BSON_VECTOR_ERROR_ARRAY_ELEMENT_TYPE = 1, + BSON_VECTOR_ERROR_ARRAY_ELEMENT_VALUE, + BSON_VECTOR_ERROR_ARRAY_KEY, + BSON_VECTOR_ERROR_MAX_SIZE, +} bson_vector_error_code_t; + + +/** @brief Implementation detail. A copy of the BSON_SUBTYPE_VECTOR header, suitable for pass-by-value. */ +typedef struct bson_vector_binary_header_impl_t { + uint8_t bytes[BSON_VECTOR_HEADER_LEN]; +} bson_vector_binary_header_impl_t; + +/** @brief Implementation detail. A reference to non-owned const BSON Binary data of subtype BSON_SUBTYPE_VECTOR */ +typedef struct bson_vector_binary_const_view_impl_t { + const uint8_t *data; + uint32_t data_len; + bson_vector_binary_header_impl_t header_copy; +} bson_vector_binary_const_view_impl_t; + +/** @brief Implementation detail. A reference to non-owned BSON Binary data of subtype BSON_SUBTYPE_VECTOR */ +typedef struct bson_vector_binary_view_impl_t { + uint8_t *data; + uint32_t data_len; + bson_vector_binary_header_impl_t header_copy; +} bson_vector_binary_view_impl_t; + +/** @brief Implementation detail. Obtain a const reference from a non-const reference without re-validating. */ +static BSON_INLINE bson_vector_binary_const_view_impl_t +bson_vector_binary_view_impl_as_const (bson_vector_binary_view_impl_t view) +{ + bson_vector_binary_const_view_impl_t result; + result.data = view.data; + result.data_len = view.data_len; + result.header_copy = view.header_copy; + return result; +} + + +/** @brief A reference to non-owned BSON Binary data holding a valid Vector of int8 element type */ +typedef struct bson_vector_int8_view_t { + bson_vector_binary_view_impl_t binary; +} bson_vector_int8_view_t; + +/** @brief A reference to non-owned const BSON Binary data holding a valid Vector of int8 element type */ +typedef struct bson_vector_int8_const_view_t { + bson_vector_binary_const_view_impl_t binary; +} bson_vector_int8_const_view_t; + +/** @brief A reference to non-owned BSON Binary data holding a valid Vector of float32 element type */ +typedef struct bson_vector_float32_view_t { + bson_vector_binary_view_impl_t binary; +} bson_vector_float32_view_t; + +/** @brief A reference to non-owned const BSON Binary data holding a valid Vector of float32 element type */ +typedef struct bson_vector_float32_const_view_t { + bson_vector_binary_const_view_impl_t binary; +} bson_vector_float32_const_view_t; + +/** @brief A reference to non-owned BSON Binary data holding a valid Vector of packed_bit */ +typedef struct bson_vector_packed_bit_view_t { + bson_vector_binary_view_impl_t binary; +} bson_vector_packed_bit_view_t; + +/** @brief A reference to non-owned const BSON Binary data holding a valid Vector of packed_bit */ +typedef struct bson_vector_packed_bit_const_view_t { + bson_vector_binary_const_view_impl_t binary; +} bson_vector_packed_bit_const_view_t; + + +static BSON_INLINE bson_vector_int8_const_view_t +bson_vector_int8_view_as_const (bson_vector_int8_view_t view) +{ + bson_vector_int8_const_view_t result; + result.binary = bson_vector_binary_view_impl_as_const (view.binary); + return result; +} + +static BSON_INLINE bson_vector_float32_const_view_t +bson_vector_float32_view_as_const (bson_vector_float32_view_t view) +{ + bson_vector_float32_const_view_t result; + result.binary = bson_vector_binary_view_impl_as_const (view.binary); + return result; +} + +static BSON_INLINE bson_vector_packed_bit_const_view_t +bson_vector_packed_bit_view_as_const (bson_vector_packed_bit_view_t view) +{ + bson_vector_packed_bit_const_view_t result; + result.binary = bson_vector_binary_view_impl_as_const (view.binary); + return result; +} + + +BSON_EXPORT (bool) +bson_vector_int8_view_init (bson_vector_int8_view_t *view_out, uint8_t *binary_data, uint32_t binary_data_len); + +BSON_EXPORT (bool) +bson_vector_int8_const_view_init (bson_vector_int8_const_view_t *view_out, + const uint8_t *binary_data, + uint32_t binary_data_len); + +BSON_EXPORT (bool) +bson_vector_float32_view_init (bson_vector_float32_view_t *view_out, uint8_t *binary_data, uint32_t binary_data_len); + + +BSON_EXPORT (bool) +bson_vector_float32_const_view_init (bson_vector_float32_const_view_t *view_out, + const uint8_t *binary_data, + uint32_t binary_data_len); + +BSON_EXPORT (bool) +bson_vector_packed_bit_view_init (bson_vector_packed_bit_view_t *view_out, + uint8_t *binary_data, + uint32_t binary_data_len); + +BSON_EXPORT (bool) +bson_vector_packed_bit_const_view_init (bson_vector_packed_bit_const_view_t *view_out, + const uint8_t *binary_data, + uint32_t binary_data_len); + + +BSON_EXPORT (bool) +bson_vector_int8_view_from_iter (bson_vector_int8_view_t *view_out, bson_iter_t *iter); + +BSON_EXPORT (bool) +bson_vector_int8_const_view_from_iter (bson_vector_int8_const_view_t *view_out, const bson_iter_t *iter); + +BSON_EXPORT (bool) +bson_vector_float32_view_from_iter (bson_vector_float32_view_t *view_out, bson_iter_t *iter); + +BSON_EXPORT (bool) +bson_vector_float32_const_view_from_iter (bson_vector_float32_const_view_t *view_out, const bson_iter_t *iter); + +BSON_EXPORT (bool) +bson_vector_packed_bit_view_from_iter (bson_vector_packed_bit_view_t *view_out, bson_iter_t *iter); + +BSON_EXPORT (bool) +bson_vector_packed_bit_const_view_from_iter (bson_vector_packed_bit_const_view_t *view_out, const bson_iter_t *iter); + + +BSON_EXPORT (bool) +bson_array_builder_append_vector_int8_elements (struct _bson_array_builder_t *builder, + bson_vector_int8_const_view_t view); + +BSON_EXPORT (bool) +bson_array_builder_append_vector_float32_elements (struct _bson_array_builder_t *builder, + bson_vector_float32_const_view_t view); + +BSON_EXPORT (bool) +bson_array_builder_append_vector_packed_bit_elements (struct _bson_array_builder_t *builder, + bson_vector_packed_bit_const_view_t view); + +BSON_EXPORT (bool) +bson_array_builder_append_vector_elements (struct _bson_array_builder_t *builder, const bson_iter_t *iter); + + +BSON_EXPORT (bool) +bson_append_vector_int8_uninit ( + bson_t *bson, const char *key, int key_length, size_t element_count, bson_vector_int8_view_t *view_out); + +#define BSON_APPEND_VECTOR_INT8_UNINIT(b, key, count, view) \ + bson_append_vector_int8_uninit (b, key, (int) strlen (key), count, view) + +BSON_EXPORT (bool) +bson_append_vector_float32_uninit ( + bson_t *bson, const char *key, int key_length, size_t element_count, bson_vector_float32_view_t *view_out); + +#define BSON_APPEND_VECTOR_FLOAT32_UNINIT(b, key, count, view) \ + bson_append_vector_float32_uninit (b, key, (int) strlen (key), count, view) + +BSON_EXPORT (bool) +bson_append_vector_packed_bit_uninit ( + bson_t *bson, const char *key, int key_length, size_t element_count, bson_vector_packed_bit_view_t *view_out); + +#define BSON_APPEND_VECTOR_PACKED_BIT_UNINIT(b, key, count, view) \ + bson_append_vector_packed_bit_uninit (b, key, (int) strlen (key), count, view) + + +BSON_EXPORT (bool) +bson_append_vector_int8_from_array ( + bson_t *bson, const char *key, int key_length, const bson_iter_t *iter, bson_error_t *error); + +#define BSON_APPEND_VECTOR_INT8_FROM_ARRAY(b, key, iter, err) \ + bson_append_vector_int8_from_array (b, key, (int) strlen (key), iter, err) + +BSON_EXPORT (bool) +bson_append_vector_float32_from_array ( + bson_t *bson, const char *key, int key_length, const bson_iter_t *iter, bson_error_t *error); + +#define BSON_APPEND_VECTOR_FLOAT32_FROM_ARRAY(b, key, iter, err) \ + bson_append_vector_float32_from_array (b, key, (int) strlen (key), iter, err) + +BSON_EXPORT (bool) +bson_append_vector_packed_bit_from_array ( + bson_t *bson, const char *key, int key_length, const bson_iter_t *iter, bson_error_t *error); + +#define BSON_APPEND_VECTOR_PACKED_BIT_FROM_ARRAY(b, key, iter, err) \ + bson_append_vector_packed_bit_from_array (b, key, (int) strlen (key), iter, err) + + +BSON_EXPORT (bool) +bson_append_array_from_vector_int8 (bson_t *bson, const char *key, int key_length, bson_vector_int8_const_view_t view); + +#define BSON_APPEND_ARRAY_FROM_VECTOR_INT8(b, key, view) \ + bson_append_array_from_vector_int8 (b, key, (int) strlen (key), view) + +BSON_EXPORT (bool) +bson_append_array_from_vector_float32 (bson_t *bson, + const char *key, + int key_length, + bson_vector_float32_const_view_t view); + +#define BSON_APPEND_ARRAY_FROM_VECTOR_FLOAT32(b, key, view) \ + bson_append_array_from_vector_float32 (b, key, (int) strlen (key), view) + +BSON_EXPORT (bool) +bson_append_array_from_vector_packed_bit (bson_t *bson, + const char *key, + int key_length, + bson_vector_packed_bit_const_view_t view); + +#define BSON_APPEND_ARRAY_FROM_VECTOR_PACKED_BIT(b, key, view) \ + bson_append_array_from_vector_packed_bit (b, key, (int) strlen (key), view) + + +static BSON_INLINE const int8_t * +bson_vector_int8_const_view_pointer (bson_vector_int8_const_view_t view) +{ + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN + return (const int8_t *) (view.binary.data + BSON_VECTOR_HEADER_LEN); + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END +} + +static BSON_INLINE int8_t * +bson_vector_int8_view_pointer (bson_vector_int8_view_t view) +{ + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN + return (int8_t *) (view.binary.data + BSON_VECTOR_HEADER_LEN); + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END +} + + +static BSON_INLINE uint32_t +bson_vector_int8_binary_data_length (size_t element_count) +{ + const size_t max_representable = (size_t) UINT32_MAX - (size_t) BSON_VECTOR_HEADER_LEN; + return element_count > max_representable ? 0u : (uint32_t) element_count + (uint32_t) BSON_VECTOR_HEADER_LEN; +} + +static BSON_INLINE uint32_t +bson_vector_float32_binary_data_length (size_t element_count) +{ + const size_t max_representable = ((size_t) UINT32_MAX - (size_t) BSON_VECTOR_HEADER_LEN) / sizeof (float); + return element_count > max_representable + ? 0u + : (uint32_t) element_count * sizeof (float) + (uint32_t) BSON_VECTOR_HEADER_LEN; +} + +static BSON_INLINE uint32_t +bson_vector_packed_bit_binary_data_length (size_t element_count) +{ + const size_t max_representable = (size_t) BSON_MIN ( + (uint64_t) SIZE_MAX, ((uint64_t) UINT32_MAX - (uint64_t) BSON_VECTOR_HEADER_LEN) * (uint64_t) 8); + return element_count > max_representable + ? 0u + : (uint32_t) ((element_count + (size_t) 7) / (size_t) 8) + (uint32_t) BSON_VECTOR_HEADER_LEN; +} + + +static BSON_INLINE size_t +bson_vector_int8_const_view_length (bson_vector_int8_const_view_t view) +{ + return view.binary.data_len - (uint32_t) BSON_VECTOR_HEADER_LEN; +} + +static BSON_INLINE size_t +bson_vector_int8_view_length (bson_vector_int8_view_t view) +{ + return bson_vector_int8_const_view_length (bson_vector_int8_view_as_const (view)); +} + +static BSON_INLINE size_t +bson_vector_float32_const_view_length (bson_vector_float32_const_view_t view) +{ + return (view.binary.data_len - (uint32_t) BSON_VECTOR_HEADER_LEN) / (uint32_t) sizeof (float); +} + +static BSON_INLINE size_t +bson_vector_float32_view_length (bson_vector_float32_view_t view) +{ + return bson_vector_float32_const_view_length (bson_vector_float32_view_as_const (view)); +} + +static BSON_INLINE size_t +bson_vector_packed_bit_const_view_length_bytes (bson_vector_packed_bit_const_view_t view) +{ + return view.binary.data_len - (uint32_t) BSON_VECTOR_HEADER_LEN; +} + +static BSON_INLINE size_t +bson_vector_packed_bit_view_length_bytes (bson_vector_packed_bit_view_t view) +{ + return bson_vector_packed_bit_const_view_length_bytes (bson_vector_packed_bit_view_as_const (view)); +} + +// Implementation detail, not part of documented API. +static BSON_INLINE size_t +bson_vector_padding_from_header_byte_1 (uint8_t byte_1) +{ + return byte_1 & 7; +} + +static BSON_INLINE size_t +bson_vector_packed_bit_const_view_padding (bson_vector_packed_bit_const_view_t view) +{ + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN + return bson_vector_padding_from_header_byte_1 (view.binary.header_copy.bytes[1]); + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END +} + +static BSON_INLINE size_t +bson_vector_packed_bit_view_padding (bson_vector_packed_bit_view_t view) +{ + return bson_vector_packed_bit_const_view_padding (bson_vector_packed_bit_view_as_const (view)); +} + +static BSON_INLINE size_t +bson_vector_packed_bit_const_view_length (bson_vector_packed_bit_const_view_t view) +{ + return bson_vector_packed_bit_const_view_length_bytes (view) * 8u - bson_vector_packed_bit_const_view_padding (view); +} + +static BSON_INLINE size_t +bson_vector_packed_bit_view_length (bson_vector_packed_bit_view_t view) +{ + return bson_vector_packed_bit_const_view_length (bson_vector_packed_bit_view_as_const (view)); +} + + +static BSON_INLINE bool +bson_vector_int8_const_view_read (bson_vector_int8_const_view_t view, + int8_t *BSON_RESTRICT values_out, + size_t element_count, + size_t vector_offset_elements) +{ + size_t length = bson_vector_int8_const_view_length (view); + if (BSON_LIKELY (vector_offset_elements <= length && element_count <= length - vector_offset_elements)) { + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN + memcpy (values_out, bson_vector_int8_const_view_pointer (view) + vector_offset_elements, element_count); + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END + return true; + } else { + return false; + } +} + +static BSON_INLINE bool +bson_vector_int8_view_read (bson_vector_int8_view_t view, + int8_t *BSON_RESTRICT values_out, + size_t element_count, + uint32_t vector_offset_elements) +{ + return bson_vector_int8_const_view_read ( + bson_vector_int8_view_as_const (view), values_out, element_count, vector_offset_elements); +} + +static BSON_INLINE bool +bson_vector_int8_view_write (bson_vector_int8_view_t view, + const int8_t *BSON_RESTRICT values, + size_t element_count, + size_t vector_offset_elements) +{ + size_t length = bson_vector_int8_view_length (view); + if (BSON_LIKELY (vector_offset_elements <= length && element_count <= length - vector_offset_elements)) { + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN + memcpy (bson_vector_int8_view_pointer (view) + vector_offset_elements, values, element_count); + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END + return true; + } else { + return false; + } +} + + +BSON_STATIC_ASSERT2 (float_is_float32, sizeof (float) == 4); + +static BSON_INLINE bool +bson_vector_float32_const_view_read (bson_vector_float32_const_view_t view, + float *BSON_RESTRICT values_out, + size_t element_count, + size_t vector_offset_elements) +{ + size_t length = bson_vector_float32_const_view_length (view); + if (BSON_LIKELY (vector_offset_elements <= length && element_count <= length - vector_offset_elements)) { + size_t byte_offset = BSON_VECTOR_HEADER_LEN + vector_offset_elements * 4; + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN +#if BSON_BYTE_ORDER == BSON_LITTLE_ENDIAN + memcpy (values_out, view.binary.data + byte_offset, element_count * 4); +#else + size_t i; + for (i = 0; i < element_count; i++) { + float aligned_tmp; + memcpy (&aligned_tmp, view.binary.data + byte_offset + i * 4, 4); + values_out[i] = BSON_FLOAT_FROM_LE (aligned_tmp); + } +#endif + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END + return true; + } else { + return false; + } +} + +static BSON_INLINE bool +bson_vector_float32_view_read (bson_vector_float32_view_t view, + float *BSON_RESTRICT values_out, + size_t element_count, + size_t vector_offset_elements) +{ + return bson_vector_float32_const_view_read ( + bson_vector_float32_view_as_const (view), values_out, element_count, vector_offset_elements); +} + +static BSON_INLINE bool +bson_vector_float32_view_write (bson_vector_float32_view_t view, + const float *BSON_RESTRICT values, + size_t element_count, + size_t vector_offset_elements) +{ + size_t length = bson_vector_float32_view_length (view); + if (BSON_LIKELY (vector_offset_elements <= length && element_count <= length - vector_offset_elements)) { + size_t byte_offset = BSON_VECTOR_HEADER_LEN + vector_offset_elements * 4; + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN +#if BSON_BYTE_ORDER == BSON_LITTLE_ENDIAN + memcpy (view.binary.data + byte_offset, values, element_count * 4); +#else + size_t i; + for (i = 0; i < element_count; i++) { + float aligned_tmp = BSON_FLOAT_TO_LE (values[i]); + memcpy (view.binary.data + byte_offset + i * 4, &aligned_tmp, 4); + } +#endif + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END + return true; + } else { + return false; + } +} + + +static BSON_INLINE bool +bson_vector_packed_bit_const_view_read_packed (bson_vector_packed_bit_const_view_t view, + uint8_t *BSON_RESTRICT packed_values_out, + size_t byte_count, + size_t vector_offset_bytes) +{ + size_t length_bytes = bson_vector_packed_bit_const_view_length_bytes (view); + if (BSON_LIKELY (vector_offset_bytes <= length_bytes && byte_count <= length_bytes - vector_offset_bytes)) { + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN + memcpy (packed_values_out, view.binary.data + BSON_VECTOR_HEADER_LEN + vector_offset_bytes, byte_count); + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END + return true; + } else { + return false; + } +} + +static BSON_INLINE bool +bson_vector_packed_bit_view_read_packed (bson_vector_packed_bit_view_t view, + uint8_t *BSON_RESTRICT packed_values_out, + size_t byte_count, + size_t vector_offset_bytes) +{ + return bson_vector_packed_bit_const_view_read_packed ( + bson_vector_packed_bit_view_as_const (view), packed_values_out, byte_count, vector_offset_bytes); +} + +static BSON_INLINE bool +bson_vector_packed_bit_view_write_packed (bson_vector_packed_bit_view_t view, + const uint8_t *BSON_RESTRICT packed_values, + size_t byte_count, + size_t vector_offset_bytes) +{ + size_t length_bytes = bson_vector_packed_bit_view_length_bytes (view); + if (BSON_LIKELY (vector_offset_bytes <= length_bytes && byte_count <= length_bytes - vector_offset_bytes)) { + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN + if (byte_count == length_bytes - vector_offset_bytes && byte_count >= 1u) { + // This write touches the last byte in the vector: + // special-case that byte so we can ensure unused bits remain set to zero. + size_t other_bytes = byte_count - 1u; + memcpy (view.binary.data + BSON_VECTOR_HEADER_LEN + vector_offset_bytes, packed_values, other_bytes); + view.binary.data[BSON_VECTOR_HEADER_LEN + vector_offset_bytes + other_bytes] = + (UINT8_C (0xFF) << bson_vector_packed_bit_view_padding (view)) & packed_values[other_bytes]; + } else { + memcpy (view.binary.data + BSON_VECTOR_HEADER_LEN + vector_offset_bytes, packed_values, byte_count); + } + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END + return true; + } else { + return false; + } +} + + +static BSON_INLINE bool +bson_vector_packed_bit_const_view_unpack_bool (bson_vector_packed_bit_const_view_t view, + bool *BSON_RESTRICT unpacked_values_out, + size_t element_count, + size_t vector_offset_elements) +{ + size_t length = bson_vector_packed_bit_const_view_length (view); + if (BSON_LIKELY (vector_offset_elements <= length && element_count <= length - vector_offset_elements)) { + size_t i; + for (i = 0; i < element_count; i++) { + size_t element_index = vector_offset_elements + i; + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN + uint8_t packed_byte = view.binary.data[BSON_VECTOR_HEADER_LEN + (element_index >> 3)]; + unpacked_values_out[i] = 0 != (packed_byte & ((uint8_t) 0x80 >> (element_index & 7))); + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END + } + return true; + } else { + return false; + } +} + +static BSON_INLINE bool +bson_vector_packed_bit_view_unpack_bool (bson_vector_packed_bit_view_t view, + bool *BSON_RESTRICT unpacked_values_out, + size_t element_count, + size_t vector_offset_elements) +{ + return bson_vector_packed_bit_const_view_unpack_bool ( + bson_vector_packed_bit_view_as_const (view), unpacked_values_out, element_count, vector_offset_elements); +} + +static BSON_INLINE bool +bson_vector_packed_bit_view_pack_bool (bson_vector_packed_bit_view_t view, + const bool *BSON_RESTRICT unpacked_values, + size_t element_count, + size_t vector_offset_elements) +{ + size_t length = bson_vector_packed_bit_view_length (view); + if (BSON_LIKELY (vector_offset_elements <= length && element_count <= length - vector_offset_elements)) { + while (element_count > 0) { + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_BEGIN + uint8_t *BSON_RESTRICT packed_byte = BSON_VECTOR_HEADER_LEN + (vector_offset_elements >> 3) + view.binary.data; + if (element_count >= 8 && (vector_offset_elements & 7) == 0) { + uint8_t complete_byte = 0; + unsigned i; + for (i = 0; i < 8; i++) { + complete_byte |= unpacked_values[i] ? ((uint8_t) 0x80 >> i) : 0; + } + *packed_byte = complete_byte; + unpacked_values += 8; + vector_offset_elements += 8; + element_count -= 8; + } else { + uint8_t mask = (uint8_t) 0x80 >> (vector_offset_elements & 7); + *packed_byte = (*packed_byte & ~mask) | (*unpacked_values ? mask : 0); + unpacked_values++; + vector_offset_elements++; + element_count--; + } + BSON_DISABLE_UNSAFE_BUFFER_USAGE_WARNING_END + } + return true; + } else { + return false; + } +} + + +BSON_END_DECLS + +#endif /* BSON_VECTOR_H */ diff --git a/src/libbson/src/bson/bson.c b/src/libbson/src/bson/bson.c index c002a6d51f0..605d5de535a 100644 --- a/src/libbson/src/bson/bson.c +++ b/src/libbson/src/bson/bson.c @@ -298,7 +298,7 @@ _bson_encode_length (bson_t *bson) /* IN */ typedef struct _bson_append_bytes_arg { - const uint8_t *bytes; // Not null. + const uint8_t *bytes; // Optional. uint32_t length; // > 0. } _bson_append_bytes_arg; @@ -369,7 +369,9 @@ BSON_STATIC_ASSERT2 (max_alloc_grow_fits_min_sizet, (uint64_t) BSON_MAX_SIZE * 2 } else { \ uint8_t *data = _bson_data ((_bson)) + ((_bson)->len - 1u); \ for (const _bson_append_bytes_arg *arg = (_list).args; arg != (_list).current; ++arg) { \ - memcpy (data, arg->bytes, arg->length); \ + if (arg->bytes) { \ + memcpy (data, arg->bytes, arg->length); \ + } \ (_bson)->len += arg->length; \ data += arg->length; \ } \ @@ -744,16 +746,15 @@ bson_append_array (bson_t *bson, /* IN */ /* *-------------------------------------------------------------------------- * - * bson_append_binary -- + * _bson_append_binary -- * - * Append binary data to @bson. The field will have the - * BSON_TYPE_BINARY type. + * Append a BSON_TYPE_BINARY field, optionally copying @binary into the field. * * Parameters: * @subtype: the BSON Binary Subtype. See bsonspec.org for more * information. - * @binary: a pointer to the raw binary data. - * @length: the size of @binary in bytes. + * @binary: Optional pointer to the raw binary data. + * @length: the size of the field's binary data in bytes. * * Returns: * true if successful; otherwise false. @@ -764,22 +765,19 @@ bson_append_array (bson_t *bson, /* IN */ *-------------------------------------------------------------------------- */ -bool -bson_append_binary (bson_t *bson, /* IN */ - const char *key, /* IN */ - int key_length, /* IN */ - bson_subtype_t subtype, /* IN */ - const uint8_t *binary, /* IN */ - uint32_t length) /* IN */ +static bool +_bson_append_binary (bson_t *bson, /* IN */ + const char *key, /* IN */ + int key_length, /* IN */ + bson_subtype_t subtype, /* IN */ + const uint8_t *binary, /* IN */ + uint32_t length) /* IN */ { static const uint8_t type = BSON_TYPE_BINARY; BSON_ASSERT_PARAM (bson); BSON_ASSERT_PARAM (key); - - if (!binary && length > 0u) { - return false; - } + BSON_OPTIONAL_PARAM (binary); BSON_APPEND_BYTES_LIST_DECLARE (args); @@ -820,6 +818,87 @@ bson_append_binary (bson_t *bson, /* IN */ } +/* + *-------------------------------------------------------------------------- + * + * bson_append_binary -- + * + * Append binary data to @bson. The field will have the + * BSON_TYPE_BINARY type. + * + * Parameters: + * @subtype: the BSON Binary Subtype. See bsonspec.org for more + * information. + * @binary: a pointer to the raw binary data. + * @length: the size of @binary in bytes. + * + * Returns: + * true if successful; otherwise false. + * + * Side effects: + * None. + * + *-------------------------------------------------------------------------- + */ + +bool +bson_append_binary (bson_t *bson, /* IN */ + const char *key, /* IN */ + int key_length, /* IN */ + bson_subtype_t subtype, /* IN */ + const uint8_t *binary, /* IN */ + uint32_t length) /* IN */ +{ + if (!binary && length > 0u) { + return false; + } + return _bson_append_binary (bson, key, key_length, subtype, binary, length); +} + + +/* + *-------------------------------------------------------------------------- + * + * bson_append_binary_uninit -- + * + * Append binary data to @bson by framing an uninitialized field to be written by the caller. + * The field will have the BSON_TYPE_BINARY type. On success, the caller MUST write to all + * bytes in the binary data field. The returned `*binary` pointer may be invalidated by + * subsequent modifications to @bson. + * + * Parameters: + * @subtype: the BSON Binary Subtype. See bsonspec.org for more + * information. + * @binary: Output parameter for a temporary pointer where the binary item's contents must be written. + * @length: the size of @binary in bytes. + * + * Returns: + * true if successful; otherwise false. + * + * Side effects: + * None. + * + *-------------------------------------------------------------------------- + */ + +bool +bson_append_binary_uninit (bson_t *bson, /* IN */ + const char *key, /* IN */ + int key_length, /* IN */ + bson_subtype_t subtype, /* IN */ + uint8_t **binary, /* IN */ + uint32_t length) /* IN */ +{ + BSON_ASSERT_PARAM (binary); + if (_bson_append_binary (bson, key, key_length, subtype, NULL, length)) { + *binary = _bson_data (bson) + bson->len - 1u - length; + return true; + } else { + return false; + } +} + + /* *-------------------------------------------------------------------------- * @@ -2790,6 +2869,7 @@ bson_array_builder_append_value (bson_array_builder_t *bab, const bson_value_t * bson_array_builder_append_impl (bson_append_value, value); } + bool bson_array_builder_append_array (bson_array_builder_t *bab, const bson_t *array) { @@ -2797,6 +2877,13 @@ bson_array_builder_append_array (bson_array_builder_t *bab, const bson_t *array) } +bool +bson_array_builder_append_array_from_vector (bson_array_builder_t *bab, const bson_iter_t *iter) +{ + bson_array_builder_append_impl (bson_append_array_from_vector, iter); +} + + bool bson_array_builder_append_binary (bson_array_builder_t *bab, bson_subtype_t subtype, @@ -2807,6 +2894,16 @@ bson_array_builder_append_binary (bson_array_builder_t *bab, } +bool +bson_array_builder_append_binary_uninit (bson_array_builder_t *bab, + bson_subtype_t subtype, + uint8_t **binary, + uint32_t length) +{ + bson_array_builder_append_impl (bson_append_binary_uninit, subtype, binary, length); +} + + bool bson_array_builder_append_bool (bson_array_builder_t *bab, bool value) { diff --git a/src/libbson/src/bson/bson.h b/src/libbson/src/bson/bson.h index 3517e216329..adfc7198f2f 100644 --- a/src/libbson/src/bson/bson.h +++ b/src/libbson/src/bson/bson.h @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -625,11 +626,31 @@ bson_append_array (bson_t *bson, const char *key, int key_length, const bson_t * BSON_EXPORT (bool) bson_array_builder_append_array (bson_array_builder_t *bab, const bson_t *array); +/** + * bson_append_array_from_vector: + * @bson: A bson_t that will be modified. + * @key: The key for the field. + * @iter: A bson_iter_t pointing to any supported vector in another bson_t. + * + * If @iter points to a supported vector type, converts the vector to a BSON array appended to @bson. + * + * Returns: true if successful; false if append would overflow max size or @iter does not point to a vector in a + * supported format. + */ +BSON_EXPORT (bool) +bson_append_array_from_vector (bson_t *bson, const char *key, int key_length, const bson_iter_t *iter); + +#define BSON_APPEND_ARRAY_FROM_VECTOR(b, key, iter) bson_append_array_from_vector (b, key, (int) strlen (key), iter) + +BSON_EXPORT (bool) +bson_array_builder_append_array_from_vector (bson_array_builder_t *bab, const bson_iter_t *iter); + /** * bson_append_binary: - * @bson: A bson_t to append. + * @bson: A bson_t. * @key: The key for the field. - * @subtype: The bson_subtype_t of the binary. + * @key_length: Optional length of 'key' in bytes, or -1 to use strlen(key). + * @subtype: The bson_subtype_t of the binary item. * @binary: The binary buffer to append. * @length: The length of @binary. * @@ -649,6 +670,30 @@ bson_array_builder_append_binary (bson_array_builder_t *bab, const uint8_t *binary, uint32_t length); +/** + * bson_append_binary_uninit: + * @bson: A bson_t. + * @key: The key for the field. + * @key_length: Optional length of 'key' in bytes, or -1 to use strlen(key). + * @binary: Output parameter, pointer for the binary data within bson_t to be written. + * @length: Length of the binary field to allocate, in bytes. + * + * Returns: true if successful; false if append would overflow max size. + */ + +BSON_EXPORT (bool) +bson_append_binary_uninit ( + bson_t *bson, const char *key, int key_length, bson_subtype_t subtype, uint8_t **binary, uint32_t length); + +#define BSON_APPEND_BINARY_UNINIT(b, key, subtype, val, len) \ + bson_append_binary_uninit (b, key, (int) strlen (key), subtype, val, len) + +BSON_EXPORT (bool) +bson_array_builder_append_binary_uninit (bson_array_builder_t *bab, + bson_subtype_t subtype, + uint8_t **binary, + uint32_t length); + /** * bson_append_bool: * @bson: A bson_t. diff --git a/src/libbson/tests/json/bson_binary_vector/float32.json b/src/libbson/tests/json/bson_binary_vector/float32.json new file mode 100644 index 00000000000..845f504ff3c --- /dev/null +++ b/src/libbson/tests/json/bson_binary_vector/float32.json @@ -0,0 +1,65 @@ +{ + "description": "Tests of Binary subtype 9, Vectors, with dtype FLOAT32", + "test_key": "vector", + "tests": [ + { + "description": "Simple Vector FLOAT32", + "valid": true, + "vector": [127.0, 7.0], + "dtype_hex": "0x27", + "dtype_alias": "FLOAT32", + "padding": 0, + "canonical_bson": "1C00000005766563746F72000A0000000927000000FE420000E04000" + }, + { + "description": "Vector with decimals and negative value FLOAT32", + "valid": true, + "vector": [127.7, -7.7], + "dtype_hex": "0x27", + "dtype_alias": "FLOAT32", + "padding": 0, + "canonical_bson": "1C00000005766563746F72000A0000000927006666FF426666F6C000" + }, + { + "description": "Empty Vector FLOAT32", + "valid": true, + "vector": [], + "dtype_hex": "0x27", + "dtype_alias": "FLOAT32", + "padding": 0, + "canonical_bson": "1400000005766563746F72000200000009270000" + }, + { + "description": "Infinity Vector FLOAT32", + "valid": true, + "vector": ["-inf", 0.0, "inf"], + "dtype_hex": "0x27", + "dtype_alias": "FLOAT32", + "padding": 0, + "canonical_bson": "2000000005766563746F72000E000000092700000080FF000000000000807F00" + }, + { + "description": "FLOAT32 with padding", + "valid": false, + "vector": [127.0, 7.0], + "dtype_hex": "0x27", + "dtype_alias": "FLOAT32", + "padding": 3, + "canonical_bson": "1C00000005766563746F72000A0000000927030000FE420000E04000" + }, + { + "description": "Insufficient vector data with 3 bytes FLOAT32", + "valid": false, + "dtype_hex": "0x27", + "dtype_alias": "FLOAT32", + "canonical_bson": "1700000005766563746F7200050000000927002A2A2A00" + }, + { + "description": "Insufficient vector data with 5 bytes FLOAT32", + "valid": false, + "dtype_hex": "0x27", + "dtype_alias": "FLOAT32", + "canonical_bson": "1900000005766563746F7200070000000927002A2A2A2A2A00" + } + ] +} diff --git a/src/libbson/tests/json/bson_binary_vector/int8.json b/src/libbson/tests/json/bson_binary_vector/int8.json new file mode 100644 index 00000000000..29524fb6178 --- /dev/null +++ b/src/libbson/tests/json/bson_binary_vector/int8.json @@ -0,0 +1,57 @@ +{ + "description": "Tests of Binary subtype 9, Vectors, with dtype INT8", + "test_key": "vector", + "tests": [ + { + "description": "Simple Vector INT8", + "valid": true, + "vector": [127, 7], + "dtype_hex": "0x03", + "dtype_alias": "INT8", + "padding": 0, + "canonical_bson": "1600000005766563746F7200040000000903007F0700" + }, + { + "description": "Empty Vector INT8", + "valid": true, + "vector": [], + "dtype_hex": "0x03", + "dtype_alias": "INT8", + "padding": 0, + "canonical_bson": "1400000005766563746F72000200000009030000" + }, + { + "description": "Overflow Vector INT8", + "valid": false, + "vector": [128], + "dtype_hex": "0x03", + "dtype_alias": "INT8", + "padding": 0 + }, + { + "description": "Underflow Vector INT8", + "valid": false, + "vector": [-129], + "dtype_hex": "0x03", + "dtype_alias": "INT8", + "padding": 0 + }, + { + "description": "INT8 with padding", + "valid": false, + "vector": [127, 7], + "dtype_hex": "0x03", + "dtype_alias": "INT8", + "padding": 3, + "canonical_bson": "1600000005766563746F7200040000000903037F0700" + }, + { + "description": "INT8 with float inputs", + "valid": false, + "vector": [127.77, 7.77], + "dtype_hex": "0x03", + "dtype_alias": "INT8", + "padding": 0 + } + ] +} diff --git a/src/libbson/tests/json/bson_binary_vector/packed_bit.json b/src/libbson/tests/json/bson_binary_vector/packed_bit.json new file mode 100644 index 00000000000..a220e7e318e --- /dev/null +++ b/src/libbson/tests/json/bson_binary_vector/packed_bit.json @@ -0,0 +1,83 @@ +{ + "description": "Tests of Binary subtype 9, Vectors, with dtype PACKED_BIT", + "test_key": "vector", + "tests": [ + { + "description": "Padding specified with no vector data PACKED_BIT", + "valid": false, + "vector": [], + "dtype_hex": "0x10", + "dtype_alias": "PACKED_BIT", + "padding": 1, + "canonical_bson": "1400000005766563746F72000200000009100100" + }, + { + "description": "Simple Vector PACKED_BIT", + "valid": true, + "vector": [127, 7], + "dtype_hex": "0x10", + "dtype_alias": "PACKED_BIT", + "padding": 0, + "canonical_bson": "1600000005766563746F7200040000000910007F0700" + }, + { + "description": "Empty Vector PACKED_BIT", + "valid": true, + "vector": [], + "dtype_hex": "0x10", + "dtype_alias": "PACKED_BIT", + "padding": 0, + "canonical_bson": "1400000005766563746F72000200000009100000" + }, + { + "description": "PACKED_BIT with padding", + "valid": true, + "vector": [127, 7], + "dtype_hex": "0x10", + "dtype_alias": "PACKED_BIT", + "padding": 3, + "canonical_bson": "1600000005766563746F7200040000000910037F0700" + }, + { + "description": "Overflow Vector PACKED_BIT", + "valid": false, + "vector": [256], + "dtype_hex": "0x10", + "dtype_alias": "PACKED_BIT", + "padding": 0 + }, + { + "description": "Underflow Vector PACKED_BIT", + "valid": false, + "vector": [-1], + "dtype_hex": "0x10", + "dtype_alias": "PACKED_BIT", + "padding": 0 + }, + { + "description": "Vector with float values PACKED_BIT", + "valid": false, + "vector": [127.5], + "dtype_hex": "0x10", + "dtype_alias": "PACKED_BIT", + "padding": 0 + }, + { + "description": "Exceeding maximum padding PACKED_BIT", + "valid": false, + "vector": [1], + "dtype_hex": "0x10", + "dtype_alias": "PACKED_BIT", + "padding": 8, + "canonical_bson": "1500000005766563746F7200030000000910080100" + }, + { + "description": "Negative padding PACKED_BIT", + "valid": false, + "vector": [1], + "dtype_hex": "0x10", + "dtype_alias": "PACKED_BIT", + "padding": -1 + } + ] +} diff --git a/src/libbson/tests/json/bson_corpus/binary.json b/src/libbson/tests/json/bson_corpus/binary.json index 20aaef743b0..0e0056f3a2c 100644 --- a/src/libbson/tests/json/bson_corpus/binary.json +++ b/src/libbson/tests/json/bson_corpus/binary.json @@ -74,6 +74,36 @@ "description": "$type query operator (conflicts with legacy $binary form with $type field)", "canonical_bson": "180000000378001000000010247479706500020000000000", "canonical_extjson": "{\"x\" : { \"$type\" : {\"$numberInt\": \"2\"}}}" + }, + { + "description": "subtype 0x09 Vector FLOAT32", + "canonical_bson": "170000000578000A0000000927000000FE420000E04000", + "canonical_extjson": "{\"x\": {\"$binary\": {\"base64\": \"JwAAAP5CAADgQA==\", \"subType\": \"09\"}}}" + }, + { + "description": "subtype 0x09 Vector INT8", + "canonical_bson": "11000000057800040000000903007F0700", + "canonical_extjson": "{\"x\": {\"$binary\": {\"base64\": \"AwB/Bw==\", \"subType\": \"09\"}}}" + }, + { + "description": "subtype 0x09 Vector PACKED_BIT", + "canonical_bson": "11000000057800040000000910007F0700", + "canonical_extjson": "{\"x\": {\"$binary\": {\"base64\": \"EAB/Bw==\", \"subType\": \"09\"}}}" + }, + { + "description": "subtype 0x09 Vector (Zero-length) FLOAT32", + "canonical_bson": "0F0000000578000200000009270000", + "canonical_extjson": "{\"x\": {\"$binary\": {\"base64\": \"JwA=\", \"subType\": \"09\"}}}" + }, + { + "description": "subtype 0x09 Vector (Zero-length) INT8", + "canonical_bson": "0F0000000578000200000009030000", + "canonical_extjson": "{\"x\": {\"$binary\": {\"base64\": \"AwA=\", \"subType\": \"09\"}}}" + }, + { + "description": "subtype 0x09 Vector (Zero-length) PACKED_BIT", + "canonical_bson": "0F0000000578000200000009100000", + "canonical_extjson": "{\"x\": {\"$binary\": {\"base64\": \"EAA=\", \"subType\": \"09\"}}}" } ], "decodeErrors": [ diff --git a/src/libbson/tests/json/bson_corpus/top.json b/src/libbson/tests/json/bson_corpus/top.json index 7eca5aa70ca..9c649b5e3f0 100644 --- a/src/libbson/tests/json/bson_corpus/top.json +++ b/src/libbson/tests/json/bson_corpus/top.json @@ -96,11 +96,11 @@ }, { "description": "Bad $regularExpression (pattern is number, not string)", - "string": "{\"x\" : {\"$regularExpression\" : { \"pattern\": 42, \"$options\" : \"\"}}}" + "string": "{\"x\" : {\"$regularExpression\" : { \"pattern\": 42, \"options\" : \"\"}}}" }, { "description": "Bad $regularExpression (options are number, not string)", - "string": "{\"x\" : {\"$regularExpression\" : { \"pattern\": \"a\", \"$options\" : 0}}}" + "string": "{\"x\" : {\"$regularExpression\" : { \"pattern\": \"a\", \"options\" : 0}}}" }, { "description" : "Bad $regularExpression (missing pattern field)", diff --git a/src/libbson/tests/test-bson-vector.c b/src/libbson/tests/test-bson-vector.c new file mode 100644 index 00000000000..98115178280 --- /dev/null +++ b/src/libbson/tests/test-bson-vector.c @@ -0,0 +1,1574 @@ +/* + * Copyright 2009-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include + +#include "bsonutil/bson-parser.h" +#include "TestSuite.h" +#include "test-conveniences.h" +#include "json-test.h" + +#ifdef _MSC_VER +#define SSCANF sscanf_s +#else +#define SSCANF sscanf +#endif + +struct view_abi_reference_type { + void *data; + uint32_t data_len; + uint8_t header_0; + uint8_t header_1; +}; + +/* ABI: Make sure vector views have the expected size. */ +#define EXPECTED_VECTOR_VIEW_SIZE (sizeof (struct view_abi_reference_type)) +BSON_STATIC_ASSERT2 (sizeof_bson_vector_int8_const_view_t, + sizeof (bson_vector_int8_const_view_t) == EXPECTED_VECTOR_VIEW_SIZE); +BSON_STATIC_ASSERT2 (sizeof_bson_vector_int8_view_t, sizeof (bson_vector_int8_view_t) == EXPECTED_VECTOR_VIEW_SIZE); +BSON_STATIC_ASSERT2 (sizeof_bson_vector_float32_const_view_t, + sizeof (bson_vector_float32_const_view_t) == EXPECTED_VECTOR_VIEW_SIZE); +BSON_STATIC_ASSERT2 (sizeof_bson_vector_float32_view_t, + sizeof (bson_vector_float32_view_t) == EXPECTED_VECTOR_VIEW_SIZE); +BSON_STATIC_ASSERT2 (sizeof_bson_vector_packed_bit_const_view_t, + sizeof (bson_vector_packed_bit_const_view_t) == EXPECTED_VECTOR_VIEW_SIZE); +BSON_STATIC_ASSERT2 (sizeof_bson_vector_packed_bit_view_t, + sizeof (bson_vector_packed_bit_view_t) == EXPECTED_VECTOR_VIEW_SIZE); +#undef EXPECTED_VECTOR_VIEW_SIZE + +typedef struct vector_json_test_case_t { + char *scenario_description, *scenario_test_key; + char *test_description, *test_dtype_hex_str, *test_dtype_alias_str, *test_canonical_bson_str; + bson_t *test_vector_array; + int64_t *test_padding; + bool *test_valid; +} vector_json_test_case_t; + +static void +translate_json_test_vector (bson_t *array_in, bson_t *array_out) +{ + // Patches JSON tests until DRIVERS-3095 is resolved: + // - convert "inf" to a double +Infinity + // - convert "-inf" to a double -Infinity + + bson_iter_t iter_in; + ASSERT (bson_iter_init (&iter_in, array_in)); + bson_array_builder_t *builder = bson_array_builder_new (); + while (bson_iter_next (&iter_in)) { + if (BSON_ITER_HOLDS_UTF8 (&iter_in) && 0 == strcmp ("inf", bson_iter_utf8 (&iter_in, NULL))) { + ASSERT (bson_array_builder_append_double (builder, (double) INFINITY)); + } else if (BSON_ITER_HOLDS_UTF8 (&iter_in) && 0 == strcmp ("-inf", bson_iter_utf8 (&iter_in, NULL))) { + ASSERT (bson_array_builder_append_double (builder, (double) -INFINITY)); + } else { + ASSERT (bson_array_builder_append_iter (builder, &iter_in)); + } + } + ASSERT (bson_array_builder_build (builder, array_out)); + bson_array_builder_destroy (builder); +} + +static bool +append_vector_packed_bit_from_packed_array ( + bson_t *bson, const char *key, int key_length, const bson_iter_t *iter, int64_t padding, bson_error_t *error) +{ + // (TODO for DRIVERS-3095, DRIVERS-3097) This implements something the test covers that our API doesn't. If the test + // were modified to cover element-by-element conversion, this can be replaced with + // bson_append_vector_packed_bit_from_array. + + BSON_ASSERT_PARAM (bson); + BSON_ASSERT_PARAM (key); + BSON_ASSERT_PARAM (iter); + + size_t byte_count = 0; + { + bson_iter_t validation_iter = *iter; + while (bson_iter_next (&validation_iter)) { + if (!BSON_ITER_HOLDS_INT (&validation_iter)) { + bson_set_error (error, + BSON_ERROR_VECTOR, + BSON_VECTOR_ERROR_ARRAY_ELEMENT_TYPE, + "expected int32 or int64 in BSON array key '%s', found item type 0x%02X", + bson_iter_key (&validation_iter), + (unsigned) bson_iter_type (&validation_iter)); + return false; + } + int64_t byte_as_int64 = bson_iter_as_int64 (&validation_iter); + if (byte_as_int64 < 0 || byte_as_int64 > UINT8_MAX) { + bson_set_error (error, + BSON_ERROR_VECTOR, + BSON_VECTOR_ERROR_ARRAY_ELEMENT_VALUE, + "BSON array key '%s' value %" PRId64 " is out of range for packed byte", + bson_iter_key (&validation_iter), + byte_as_int64); + return false; + } + byte_count++; + } + } + + if (padding < 0 || padding > 7) { + bson_set_error (error, + TEST_ERROR_DOMAIN, + TEST_ERROR_CODE, + "'padding' parameter (%" PRId64 + ") for append_vector_packed_bit_from_packed_array is out of range", + padding); + return false; + } + if (byte_count < 1 && padding > 0) { + bson_set_error (error, + TEST_ERROR_DOMAIN, + TEST_ERROR_CODE, + "nonzero 'padding' parameter (%" PRId64 + ") for zero-length append_vector_packed_bit_from_packed_array", + padding); + return false; + } + + bson_vector_packed_bit_view_t view; + if (bson_append_vector_packed_bit_uninit (bson, key, key_length, byte_count * 8u - (size_t) padding, &view)) { + bson_iter_t copy_iter = *iter; + for (size_t i = 0; i < byte_count; i++) { + ASSERT (bson_iter_next (©_iter)); + uint8_t packed_byte = (uint8_t) bson_iter_as_int64 (©_iter); + ASSERT (bson_vector_packed_bit_view_write_packed (view, &packed_byte, 1, i)); + } + return true; + } else { + return false; + } +} + +static void +hex_str_to_bson (bson_t *bson_out, const char *hex_str) +{ + uint32_t size = (uint32_t) strlen (hex_str) / 2u; + uint8_t *buffer = bson_reserve_buffer (bson_out, size); + for (uint32_t i = 0; i < size; i++) { + unsigned int byte; + ASSERT (SSCANF (&hex_str[i * 2], "%2x", &byte) == 1); + buffer[i] = (uint8_t) byte; + } +} + +// Implement spec tests, given parsed arguments +static void +test_bson_vector_json_case (vector_json_test_case_t *test_case) +{ + bson_t expected_bson = BSON_INITIALIZER; + if (test_case->test_canonical_bson_str) { + hex_str_to_bson (&expected_bson, test_case->test_canonical_bson_str); + } + + ASSERT (test_case->test_valid); + ASSERT (test_case->test_dtype_hex_str); + ASSERT (test_case->scenario_test_key); + + bson_t vector_from_array = BSON_INITIALIZER; + bson_error_t vector_from_array_error; + bool vector_from_array_ok; + + // (TODO for DRIVERS-3095, DRIVERS-3097) Patch test cases that have unused bits set to '1' when '0' is required. + if (0 == strcmp ("PACKED_BIT with padding", test_case->test_description)) { + bson_iter_t iter; + ASSERT (bson_iter_init_find (&iter, &expected_bson, test_case->scenario_test_key)); + uint32_t binary_len; + uint8_t *binary; + bson_iter_overwrite_binary (&iter, BSON_SUBTYPE_VECTOR, &binary_len, &binary); + ASSERT (binary_len > BSON_VECTOR_HEADER_LEN); + binary[binary_len - 1] &= (uint8_t) 0xFF << bson_vector_padding_from_header_byte_1 (binary[1]); + } + + // Try a format conversion from array to the indicated vector format. + // The spec calls the first header byte "dtype" (combining the element type and element size fields) + if (0 == strcmp ("0x03", test_case->test_dtype_hex_str)) { + // int8 vector from int32/int64 array + bson_iter_t iter; + bool padding_ok = !test_case->test_padding || *test_case->test_padding == 0; + vector_from_array_ok = test_case->test_vector_array && padding_ok && + bson_iter_init (&iter, test_case->test_vector_array) && + BSON_APPEND_VECTOR_INT8_FROM_ARRAY ( + &vector_from_array, test_case->scenario_test_key, &iter, &vector_from_array_error); + } else if (0 == strcmp ("0x27", test_case->test_dtype_hex_str)) { + // float32 vector from float64 array + // (TODO for DRIVERS-3095) Convert the unusual numeric array format to a standard array + bson_t converted = BSON_INITIALIZER; + bool padding_ok = !test_case->test_padding || *test_case->test_padding == 0; + if (test_case->test_vector_array && padding_ok) { + bson_iter_t iter; + translate_json_test_vector (test_case->test_vector_array, &converted); + vector_from_array_ok = bson_iter_init (&iter, &converted) && + BSON_APPEND_VECTOR_FLOAT32_FROM_ARRAY ( + &vector_from_array, test_case->scenario_test_key, &iter, &vector_from_array_error); + } else { + vector_from_array_ok = false; + } + bson_destroy (&converted); + } else if (0 == strcmp ("0x10", test_case->test_dtype_hex_str)) { + // packed_bit from packed bytes in an int array, with "padding" parameter supplied separately. + // TODO for DRIVERS-3095, DRIVERS-3097: + // - Array-to-Vector should be defined as an element-by-element conversion. This test shouldn't operate on packed + // representations. + // - Include additional JSON tests for packed access, distinct from Array conversion. + // - Tests should keep the unused bits zeroed as required. + bson_iter_t iter; + if (!test_case->test_padding) { + test_error ("test '%s' is missing required 'padding' field", test_case->test_description); + } + vector_from_array_ok = test_case->test_vector_array && bson_iter_init (&iter, test_case->test_vector_array) && + append_vector_packed_bit_from_packed_array (&vector_from_array, + test_case->scenario_test_key, + -1, + &iter, + *test_case->test_padding, + &vector_from_array_error); + } else { + test_error ( + "test '%s' has unsupported dtype_hex format '%s'", test_case->test_description, test_case->test_dtype_hex_str); + } + + if (*test_case->test_valid) { + /* + * "To prove correct in a valid case (valid: true), one MUST + * - encode a document from the numeric values, dtype, and padding, along with the "test_key", and assert this + * matches the canonical_bson string. + * - decode the canonical_bson into its binary form, and then assert that the numeric values, dtype, and padding + * all match those provided in the JSON." + */ + + // Check the vector-from-array performed above ("encode") + + if (!test_case->test_vector_array) { + test_error ("test '%s' should be valid, but missing 'vector' field", test_case->test_description); + } + + if (!vector_from_array_ok) { + test_error ("test '%s' should be valid, but vector-from-array failed: %s", + test_case->test_description, + vector_from_array_error.message); + } + + if (0 != bson_compare (&vector_from_array, &expected_bson)) { + test_error ("test '%s' did not exactly match the reference document.\n " + "Actual: %s\n Expected: %s", + test_case->test_description, + tmp_json (&vector_from_array), + tmp_json (&expected_bson)); + } + + // Perform an array-from-vector and check it ("decode") + + bson_iter_t iter; + ASSERT (bson_iter_init_find (&iter, &expected_bson, test_case->scenario_test_key)); + + bson_t array_from_vector = BSON_INITIALIZER; + { + bson_array_builder_t *array_builder = bson_array_builder_new (); + if (!bson_array_builder_append_vector_elements (array_builder, &iter)) { + test_error ("test '%s' should be valid but failed array-from-vector conversion", + test_case->test_description); + } + ASSERT (bson_array_builder_build (array_builder, &array_from_vector)); + bson_array_builder_destroy (array_builder); + } + + // (TODO for DRIVERS-3095, DRIVERS-3097) Due to loosely defined element types and rounding behavior in the test + // vectors, this comparison can't be exact. Temporary special cases are needed for float32 and packed_bit. + + if (BSON_ITER_HOLDS_VECTOR_FLOAT32 (&iter)) { + // float32 special case: We need to handle the nonstandard "inf" and "-inf" forms, and + // due to underspecified rounding and conversion rules we compare value inexactly. + + bson_iter_t actual_iter, expected_iter; + ASSERT (bson_iter_init (&actual_iter, &array_from_vector)); + ASSERT (bson_iter_init (&expected_iter, test_case->test_vector_array)); + + for (size_t i = 0;; i++) { + bool actual_next = bson_iter_next (&actual_iter); + bool expected_next = bson_iter_next (&expected_iter); + if (!actual_next && !expected_next) { + break; + } else if (!actual_next) { + test_error ("converted array is shorter than expected"); + } else if (!expected_next) { + test_error ("converted array is longer than expected"); + } + + if (!BSON_ITER_HOLDS_DOUBLE (&actual_iter)) { + test_error ("converted array element %d has unexpected type, should be double", (int) i); + } + double actual_double = bson_iter_double (&actual_iter); + + double expected_double; + if (BSON_ITER_HOLDS_UTF8 (&expected_iter) && 0 == strcmp ("inf", bson_iter_utf8 (&expected_iter, NULL))) { + expected_double = (double) INFINITY; + } else if (BSON_ITER_HOLDS_UTF8 (&expected_iter) && + 0 == strcmp ("-inf", bson_iter_utf8 (&expected_iter, NULL))) { + expected_double = (double) -INFINITY; + } else if (BSON_ITER_HOLDS_DOUBLE (&expected_iter)) { + expected_double = bson_iter_double (&expected_iter); + } else { + test_error ("test-vector array element %d has unexpected type, should be double, 'inf', or '-inf'.", + (int) i); + } + + bool is_sorta_equal = false; + if (expected_double != expected_double) { + // Expect NaN, any type is fine. + if (actual_double != actual_double) { + is_sorta_equal = true; + } + } else if (expected_double == 0.0 || expected_double * 0.0 != 0.0) { + // Infinity or zero, equality comparison is fine. + is_sorta_equal = expected_double == actual_double; + } else { + // Finite number, allow +/- error relative to the scale of the expected value. + // Note that ASSERT_EQUAL_DOUBLE() in TestSuite exists but its fixed error threshold of 20% seems too + // loose for this application. + static const double allowed_relative_error = 1e-7; + double allowed_absolute_error = fabs (allowed_relative_error * expected_double); + is_sorta_equal = actual_double >= expected_double - allowed_absolute_error && + actual_double <= expected_double + allowed_absolute_error; + } + if (!is_sorta_equal) { + test_error ("test-vector array element %d failed inexact float32 match. Actual: %f Expected: %f", + (int) i, + actual_double, + expected_double); + } + } + + } else if (BSON_ITER_HOLDS_VECTOR_PACKED_BIT (&iter)) { + // packed_bit special case: The tests for packed_bit aren't actually testing vector-to-array conversion as we + // understand it, they're operating on bytes rather than elements. This is the inverse of + // append_vector_packed_bit_from_packed_array() above, and it bypasses the vector-to-array conversion. + // 'array_from_vector' is ignored on this path. + + bson_iter_t expected_iter; + ASSERT (bson_iter_init (&expected_iter, test_case->test_vector_array)); + bson_vector_packed_bit_const_view_t actual_view; + ASSERT (bson_vector_packed_bit_const_view_from_iter (&actual_view, &iter)); + + size_t byte_count = 0; + while (bson_iter_next (&expected_iter)) { + int64_t expected_byte; + if (BSON_ITER_HOLDS_INT (&expected_iter)) { + expected_byte = bson_iter_as_int64 (&expected_iter); + } else { + test_error ("test-vector array element %d has unexpected type, should be int.", (int) byte_count); + } + + // (TODO for DRIVERS-3095, DRIVERS-3097) Packed writes can't set unused bits to '1' in libbson, but the spec + // tests allow padding bits to take on undefined values. Modify the expected values to keep padding bits + // zeroed. + if (0 == strcmp ("PACKED_BIT with padding", test_case->test_description) && + byte_count == bson_vector_packed_bit_const_view_length_bytes (actual_view) - 1u) { + expected_byte &= ((int64_t) 0xFF << *test_case->test_padding) & 0xFF; + } + + // Note, the zero initializer is only needed due to a false positive -Wmaybe-uninitialized warning in + // uncommon configurations where the compiler does not have visibility into memcpy(). + uint8_t actual_byte = 0; + ASSERT (bson_vector_packed_bit_const_view_read_packed (actual_view, &actual_byte, 1, byte_count)); + + if (expected_byte != (int64_t) actual_byte) { + test_error ("failed to match packed byte %d of packed_bit test-vector. Actual: 0x%02x Expected: 0x%02x", + (int) byte_count, + (unsigned) actual_byte, + (unsigned) expected_byte); + } + byte_count++; + } + ASSERT_CMPSIZE_T (byte_count, ==, bson_vector_packed_bit_const_view_length_bytes (actual_view)); + + } else { + // No special case, expect an exact match. (Used for int8 vectors) + if (0 != bson_compare (&array_from_vector, test_case->test_vector_array)) { + test_error ("bson_binary_vector JSON scenario '%s' test '%s' did not exactly match the reference array " + "after array-from-vector.\n " + "Actual: %s\n Expected: %s", + test_case->scenario_description, + test_case->test_description, + tmp_json (&array_from_vector), + tmp_json (test_case->test_vector_array)); + } + } + + bson_destroy (&array_from_vector); + } else { + /* + * "To prove correct in an invalid case (valid:false), one MUST + * - if the vector field is present, raise an exception when attempting to encode a document from the numeric + * values, dtype, and padding. + * - if the canonical_bson field is present, raise an exception when attempting to deserialize it into the + * corresponding numeric values, as the field contains corrupted data." + */ + + if (vector_from_array_ok) { + test_error ("bson_binary_vector JSON scenario '%s' test '%s' should be invalid but vector-from-array " + "succeeded with result: %s", + test_case->scenario_description, + test_case->test_description, + tmp_json (&vector_from_array)); + } + + if (test_case->test_canonical_bson_str) { + bson_t array_from_vector = BSON_INITIALIZER; + bson_iter_t iter; + ASSERT (bson_iter_init_find (&iter, &expected_bson, test_case->scenario_test_key)); + if (BSON_APPEND_ARRAY_FROM_VECTOR (&array_from_vector, "should_fail", &iter)) { + test_error ("bson_binary_vector JSON scenario '%s' test '%s' should be invalid but array-from-vector " + "succeeded with result: %s", + test_case->scenario_description, + test_case->test_description, + tmp_json (&array_from_vector)); + } + bson_destroy (&array_from_vector); + } + } + + bson_destroy (&expected_bson); +} + +// callback for install_json_test_suite_with_check, implements JSON spec tests +static void +test_bson_vector_json_cb (void *test_arg) +{ + BSON_ASSERT_PARAM (test_arg); + bson_t *scenario = (bson_t *) test_arg; + bson_error_t error; + vector_json_test_case_t test_case; + + bson_parser_t *scenario_opts = bson_parser_new (); + bson_t *tests; + bson_parser_utf8 (scenario_opts, "description", &test_case.scenario_description); + bson_parser_utf8 (scenario_opts, "test_key", &test_case.scenario_test_key); + bson_parser_array (scenario_opts, "tests", &tests); + if (!bson_parser_parse (scenario_opts, scenario, &error)) { + test_error ("format error in bson_binary_vector JSON scenario: %s", error.message); + } + + bson_iter_t tests_iter; + ASSERT (bson_iter_init (&tests_iter, tests)); + while (bson_iter_next (&tests_iter)) { + bson_t test_subdoc; + bson_iter_bson (&tests_iter, &test_subdoc); + + bson_parser_t *test_opts = bson_parser_new (); + bson_parser_utf8 (test_opts, "description", &test_case.test_description); + bson_parser_bool (test_opts, "valid", &test_case.test_valid); + bson_parser_array_optional (test_opts, "vector", &test_case.test_vector_array); + bson_parser_utf8 (test_opts, "dtype_hex", &test_case.test_dtype_hex_str); + bson_parser_utf8 (test_opts, "dtype_alias", &test_case.test_dtype_alias_str); + bson_parser_int_optional (test_opts, "padding", &test_case.test_padding); + bson_parser_utf8_optional (test_opts, "canonical_bson", &test_case.test_canonical_bson_str); + if (!bson_parser_parse (test_opts, &test_subdoc, &error)) { + test_error ( + "format error in bson_binary_vector JSON test for '%s': %s", test_case.scenario_description, error.message); + } + + if (test_suite_debug_output ()) { + printf ("bson_binary_vector JSON scenario '%s' test '%s'\n", + test_case.scenario_description, + test_case.test_description); + } + + test_bson_vector_json_case (&test_case); + bson_parser_destroy_with_parsed_fields (test_opts); + } + bson_parser_destroy_with_parsed_fields (scenario_opts); +} + +static void +test_bson_vector_view_api_usage_int8 (void) +{ + bson_t doc = BSON_INITIALIZER; + + // Construct a small vector by writing individual elements + { + bson_vector_int8_view_t view; + const size_t length = 25; + ASSERT (BSON_APPEND_VECTOR_INT8_UNINIT (&doc, "vector", length, &view)); + for (size_t i = 0; i < length; i++) { + int8_t v = (int8_t) i - 9; + bson_vector_int8_view_write (view, &v, 1, i); + } + } + + ASSERT_CMPJSON ( + bson_as_canonical_extended_json (&doc, NULL), + BSON_STR ({"vector" : {"$binary" : {"base64" : "AwD3+Pn6+/z9/v8AAQIDBAUGBwgJCgsMDQ4P", "subType" : "09"}}})); + + // Construct a longer vector by writing individual elements + { + bson_vector_int8_view_t view; + const size_t length = 50000; + ASSERT (bson_append_vector_int8_uninit (&doc, "longer_vector", -1, length, &view)); + for (size_t i = 0; i < length; i++) { + int8_t v = (int8_t) (uint8_t) i; + bson_vector_int8_view_write (view, &v, 1, i); + } + } + + // Fail appending a vector that would be too large to represent + { + bson_vector_int8_view_t view; + const size_t length = UINT32_MAX - 1; + ASSERT (!bson_append_vector_int8_uninit (&doc, "overlong_vector", -1, length, &view)); + } + + // Use a mutable view to partially overwrite "vector" + { + bson_iter_t iter; + ASSERT (bson_iter_init_find (&iter, &doc, "vector")); + bson_vector_int8_view_t view; + ASSERT (bson_vector_int8_view_from_iter (&view, &iter)); + ASSERT (bson_vector_int8_view_length (view) == 25); + int8_t values[5] = {12, 34, 56, 78, 90}; + ASSERT (bson_vector_int8_view_write (view, values, sizeof values / sizeof values[0], 3)); + } + + // Read the modified small vector into an int8_t array + { + bson_iter_t iter; + ASSERT (bson_iter_init_find (&iter, &doc, "vector")); + ASSERT (BSON_ITER_HOLDS_BINARY (&iter)); + ASSERT (BSON_ITER_HOLDS_VECTOR (&iter)); + ASSERT (BSON_ITER_HOLDS_VECTOR_INT8 (&iter)); + ASSERT (!BSON_ITER_HOLDS_VECTOR_FLOAT32 (&iter)); + ASSERT (!BSON_ITER_HOLDS_VECTOR_PACKED_BIT (&iter)); + bson_vector_int8_const_view_t view; + ASSERT (bson_vector_int8_const_view_from_iter (&view, &iter)); + ASSERT (bson_vector_int8_const_view_length (view) == 25); + int8_t values[25]; + static const int8_t expected_values[25] = {-9, -8, -7, 12, 34, 56, 78, 90, -1, 0, 1, 2, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + ASSERT (bson_vector_int8_const_view_read (view, values, sizeof values / sizeof values[0], 0)); + ASSERT_MEMCMP (values, expected_values, (int) sizeof expected_values); + } + + // Convert the small vector to a BSON Array, and check the resulting canonical extended JSON. + // Each element will be losslessly converted to int32. + // Convert the output back, and add a "round_trip" key to the original document. + { + bson_t converted = BSON_INITIALIZER; + bson_iter_t iter; + ASSERT (bson_iter_init_find (&iter, &doc, "vector")); + ASSERT (BSON_APPEND_ARRAY_FROM_VECTOR (&converted, "array", &iter)); + ASSERT_CMPJSON (bson_as_canonical_extended_json (&converted, NULL), BSON_STR ({ + "array" : [ + {"$numberInt" : "-9"}, {"$numberInt" : "-8"}, {"$numberInt" : "-7"}, {"$numberInt" : "12"}, + {"$numberInt" : "34"}, {"$numberInt" : "56"}, {"$numberInt" : "78"}, {"$numberInt" : "90"}, + {"$numberInt" : "-1"}, {"$numberInt" : "0"}, {"$numberInt" : "1"}, {"$numberInt" : "2"}, + {"$numberInt" : "3"}, {"$numberInt" : "4"}, {"$numberInt" : "5"}, {"$numberInt" : "6"}, + {"$numberInt" : "7"}, {"$numberInt" : "8"}, {"$numberInt" : "9"}, {"$numberInt" : "10"}, + {"$numberInt" : "11"}, {"$numberInt" : "12"}, {"$numberInt" : "13"}, {"$numberInt" : "14"}, + {"$numberInt" : "15"} + ] + })); + + ASSERT (bson_iter_init_find (&iter, &converted, "array")); + ASSERT (BSON_ITER_HOLDS_ARRAY (&iter)); + ASSERT (bson_iter_recurse (&iter, &iter)); + ASSERT (BSON_APPEND_VECTOR_INT8_FROM_ARRAY (&doc, "round_trip", &iter, NULL)); + bson_destroy (&converted); + } + + // The original small vector and round_trip small vector must be identical + { + bson_iter_t a, b; + ASSERT (bson_iter_init_find (&a, &doc, "vector")); + ASSERT (bson_iter_init_find (&b, &doc, "round_trip")); + ASSERT (bson_iter_binary_equal (&a, &b)); + } + + // Try the same round trip conversion with our longer vector + // (Note that BSON arrays special-case keys below "1000") + { + bson_t converted = BSON_INITIALIZER; + bson_iter_t iter; + ASSERT (bson_iter_init_find (&iter, &doc, "longer_vector")); + ASSERT (BSON_APPEND_ARRAY_FROM_VECTOR (&converted, "array", &iter)); + ASSERT (bson_iter_init_find (&iter, &converted, "array")); + ASSERT (BSON_ITER_HOLDS_ARRAY (&iter)); + ASSERT (bson_iter_recurse (&iter, &iter)); + ASSERT (BSON_APPEND_VECTOR_INT8_FROM_ARRAY (&doc, "longer_round_trip", &iter, NULL)); + bson_destroy (&converted); + } + { + bson_iter_t a, b; + ASSERT (bson_iter_init_find (&a, &doc, "longer_vector")); + ASSERT (bson_iter_init_find (&b, &doc, "longer_round_trip")); + ASSERT (bson_iter_binary_equal (&a, &b)); + } + + bson_destroy (&doc); +} + +static void +test_bson_vector_view_api_usage_float32 (void) +{ + bson_t doc = BSON_INITIALIZER; + + // Construct a small vector by writing individual elements + { + bson_vector_float32_view_t view; + const size_t length = 5; + ASSERT (BSON_APPEND_VECTOR_FLOAT32_UNINIT (&doc, "vector", length, &view)); + for (size_t i = 0; i < length; i++) { + float v = 1.0f + 0.25f * (float) i; + bson_vector_float32_view_write (view, &v, 1, i); + } + } + + ASSERT_CMPJSON ( + bson_as_canonical_extended_json (&doc, NULL), + BSON_STR ({"vector" : {"$binary" : {"base64" : "JwAAAIA/AACgPwAAwD8AAOA/AAAAQA==", "subType" : "09"}}})); + + // Construct a longer vector by writing individual elements + { + bson_vector_float32_view_t view; + const size_t length = 10000; + ASSERT (bson_append_vector_float32_uninit (&doc, "longer_vector", -1, length, &view)); + for (size_t i = 0; i < length; i++) { + float v = (float) i; + bson_vector_float32_view_write (view, &v, 1, i); + } + } + + // Fail appending a vector that would be too large to represent + { + bson_vector_float32_view_t view; + const size_t length = (UINT32_MAX - 1) / 4; + ASSERT (!bson_append_vector_float32_uninit (&doc, "overlong_vector", -1, length, &view)); + } + + // Read the small vector into a float array + { + bson_iter_t iter; + ASSERT (bson_iter_init_find (&iter, &doc, "vector")); + ASSERT (BSON_ITER_HOLDS_BINARY (&iter)); + ASSERT (BSON_ITER_HOLDS_VECTOR (&iter)); + ASSERT (!BSON_ITER_HOLDS_VECTOR_INT8 (&iter)); + ASSERT (BSON_ITER_HOLDS_VECTOR_FLOAT32 (&iter)); + ASSERT (!BSON_ITER_HOLDS_VECTOR_PACKED_BIT (&iter)); + bson_vector_float32_const_view_t view; + ASSERT (bson_vector_float32_const_view_from_iter (&view, &iter)); + ASSERT (bson_vector_float32_const_view_length (view) == 5); + float values[5]; + static const float expected_values[5] = {1.0f, 1.25f, 1.5f, 1.75f, 2.0f}; + ASSERT (bson_vector_float32_const_view_read (view, values, sizeof values / sizeof values[0], 0)); + ASSERT_MEMCMP (values, expected_values, (int) sizeof expected_values); + } + + // Convert the small vector to a BSON Array, and check the resulting canonical extended JSON. + // Each element will be converted from 32-bit to 64-bit float. + // Convert the output back, and add a "round_trip" key to the original document. + { + bson_t converted = BSON_INITIALIZER; + bson_iter_t iter; + ASSERT (bson_iter_init_find (&iter, &doc, "vector")); + ASSERT (BSON_APPEND_ARRAY_FROM_VECTOR (&converted, "array", &iter)); + ASSERT_CMPJSON (bson_as_canonical_extended_json (&converted, NULL), BSON_STR ({ + "array" : [ + {"$numberDouble" : "1.0"}, + {"$numberDouble" : "1.25"}, + {"$numberDouble" : "1.5"}, + {"$numberDouble" : "1.75"}, + {"$numberDouble" : "2.0"} + ] + })); + + ASSERT (bson_iter_init_find (&iter, &converted, "array")); + ASSERT (BSON_ITER_HOLDS_ARRAY (&iter)); + ASSERT (bson_iter_recurse (&iter, &iter)); + ASSERT (BSON_APPEND_VECTOR_FLOAT32_FROM_ARRAY (&doc, "round_trip", &iter, NULL)); + bson_destroy (&converted); + } + + // The original small vector and round_trip small vector must be identical + { + bson_iter_t a, b; + ASSERT (bson_iter_init_find (&a, &doc, "vector")); + ASSERT (bson_iter_init_find (&b, &doc, "round_trip")); + ASSERT (bson_iter_binary_equal (&a, &b)); + } + + // Try the same round trip conversion with our longer vector + // (Note that BSON arrays special-case keys below "1000") + { + bson_t converted = BSON_INITIALIZER; + bson_iter_t iter; + ASSERT (bson_iter_init_find (&iter, &doc, "longer_vector")); + ASSERT (BSON_APPEND_ARRAY_FROM_VECTOR (&converted, "array", &iter)); + ASSERT (bson_iter_init_find (&iter, &converted, "array")); + ASSERT (BSON_ITER_HOLDS_ARRAY (&iter)); + ASSERT (bson_iter_recurse (&iter, &iter)); + ASSERT (BSON_APPEND_VECTOR_FLOAT32_FROM_ARRAY (&doc, "longer_round_trip", &iter, NULL)); + bson_destroy (&converted); + } + { + bson_iter_t a, b; + ASSERT (bson_iter_init_find (&a, &doc, "longer_vector")); + ASSERT (bson_iter_init_find (&b, &doc, "longer_round_trip")); + ASSERT (bson_iter_binary_equal (&a, &b)); + } + + bson_destroy (&doc); +} + +static void +test_bson_vector_view_api_usage_packed_bit (void) +{ + bson_t doc = BSON_INITIALIZER; + + // Construct a small vector by packing individual elements from a 'bool' source + { + bson_vector_packed_bit_view_t view; + const size_t length = 123; + ASSERT (BSON_APPEND_VECTOR_PACKED_BIT_UNINIT (&doc, "vector", length, &view)); + for (size_t i = 0; i < length; i++) { + bool v = (i & 1) != 0; + bson_vector_packed_bit_view_pack_bool (view, &v, 1, i); + } + } + + ASSERT_CMPJSON (bson_as_canonical_extended_json (&doc, NULL), + BSON_STR ({"vector" : {"$binary" : {"base64" : "EAVVVVVVVVVVVVVVVVVVVVVA", "subType" : "09"}}})); + + // Construct a longer vector by packing individual elements from a 'bool' source + { + bson_vector_packed_bit_view_t view; + const size_t length = 100002; + ASSERT (bson_append_vector_packed_bit_uninit (&doc, "longer_vector", -1, length, &view)); + for (size_t i = 0; i < length; i++) { + bool v = (i & 3) != 0; + bson_vector_packed_bit_view_pack_bool (view, &v, 1, i); + } + } + + // Fail appending a vector that would be too large to represent + { + bson_vector_int8_view_t view; + const size_t length = (UINT32_MAX - 1) * (size_t) 8; + ASSERT (!bson_append_vector_int8_uninit (&doc, "overlong_vector", -1, length, &view)); + } + + // Unpack the small vector into a bool array + { + bson_iter_t iter; + ASSERT (bson_iter_init_find (&iter, &doc, "vector")); + ASSERT (BSON_ITER_HOLDS_BINARY (&iter)); + ASSERT (BSON_ITER_HOLDS_VECTOR (&iter)); + ASSERT (!BSON_ITER_HOLDS_VECTOR_INT8 (&iter)); + ASSERT (!BSON_ITER_HOLDS_VECTOR_FLOAT32 (&iter)); + ASSERT (BSON_ITER_HOLDS_VECTOR_PACKED_BIT (&iter)); + bson_vector_packed_bit_const_view_t view; + ASSERT (bson_vector_packed_bit_const_view_from_iter (&view, &iter)); + ASSERT (bson_vector_packed_bit_const_view_length (view) == 123); + bool values[123]; + bool expected_values[123]; + for (size_t i = 0; i < sizeof expected_values / sizeof expected_values[0]; i++) { + expected_values[i] = (i & 1) != 0; + } + ASSERT (bson_vector_packed_bit_const_view_unpack_bool (view, values, sizeof values / sizeof values[0], 0)); + ASSERT_MEMCMP (values, expected_values, (int) sizeof expected_values); + } + + // Read the packed representation without unpacking + { + bson_iter_t iter; + ASSERT (bson_iter_init_find (&iter, &doc, "vector")); + bson_vector_packed_bit_const_view_t view; + ASSERT (bson_vector_packed_bit_const_view_from_iter (&view, &iter)); + ASSERT (bson_vector_packed_bit_const_view_length (view) == 123); + uint8_t packed[16]; + static const uint8_t expected_packed[16] = { + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x40}; + ASSERT (bson_vector_packed_bit_const_view_read_packed (view, packed, sizeof packed, 0)); + ASSERT_MEMCMP (packed, expected_packed, (int) sizeof expected_packed); + } + + // Partial overwrite of the packed representation + { + bson_iter_t iter; + ASSERT (bson_iter_init_find (&iter, &doc, "vector")); + bson_vector_packed_bit_view_t view; + ASSERT (bson_vector_packed_bit_view_from_iter (&view, &iter)); + uint8_t packed[2] = {0x12, 0x34}; + ASSERT (bson_vector_packed_bit_view_write_packed (view, packed, sizeof packed, 12)); + } + + // Partial read of the packed representation + { + bson_iter_t iter; + ASSERT (bson_iter_init_find (&iter, &doc, "vector")); + bson_vector_packed_bit_const_view_t view; + ASSERT (bson_vector_packed_bit_const_view_from_iter (&view, &iter)); + uint8_t packed[5]; + static const uint8_t expected_packed[5] = {0x55, 0x12, 0x34, 0x55, 0x40}; + ASSERT (bson_vector_packed_bit_const_view_read_packed (view, packed, sizeof packed, 11)); + ASSERT_MEMCMP (packed, expected_packed, (int) sizeof expected_packed); + } + + // Partial write from a bool array, spanning complete and partial packed bytes + { + bson_iter_t iter; + ASSERT (bson_iter_init_find (&iter, &doc, "vector")); + bson_vector_packed_bit_view_t view; + ASSERT (bson_vector_packed_bit_view_from_iter (&view, &iter)); + bool values[24] = { + false, false, false, true, false, false, false, true, true, true, false, true, + true, false, false, false, false, true, false, false, false, false, false, true, + }; + ASSERT (bson_vector_packed_bit_view_pack_bool (view, values, sizeof values / sizeof values[0], 3)); + } + + // Convert the small vector to a BSON Array, and check the resulting canonical extended JSON. + // Each element will be losslessly converted to int32. + // Convert the output back, and add a "round_trip" key to the original document. + { + bson_t converted = BSON_INITIALIZER; + bson_iter_t iter; + ASSERT (bson_iter_init_find (&iter, &doc, "vector")); + ASSERT (BSON_APPEND_ARRAY_FROM_VECTOR (&converted, "array", &iter)); + ASSERT_CMPJSON (bson_as_canonical_extended_json (&converted, NULL), BSON_STR ({ + "array" : [ + {"$numberInt" : "0"}, {"$numberInt" : "1"}, {"$numberInt" : "0"}, {"$numberInt" : "0"}, + {"$numberInt" : "0"}, {"$numberInt" : "0"}, {"$numberInt" : "1"}, {"$numberInt" : "0"}, + {"$numberInt" : "0"}, {"$numberInt" : "0"}, {"$numberInt" : "1"}, {"$numberInt" : "1"}, + {"$numberInt" : "1"}, {"$numberInt" : "0"}, {"$numberInt" : "1"}, {"$numberInt" : "1"}, + {"$numberInt" : "0"}, {"$numberInt" : "0"}, {"$numberInt" : "0"}, {"$numberInt" : "0"}, + {"$numberInt" : "1"}, {"$numberInt" : "0"}, {"$numberInt" : "0"}, {"$numberInt" : "0"}, + {"$numberInt" : "0"}, {"$numberInt" : "0"}, {"$numberInt" : "1"}, {"$numberInt" : "1"}, + {"$numberInt" : "0"}, {"$numberInt" : "1"}, {"$numberInt" : "0"}, {"$numberInt" : "1"}, + {"$numberInt" : "0"}, {"$numberInt" : "1"}, {"$numberInt" : "0"}, {"$numberInt" : "1"}, + {"$numberInt" : "0"}, {"$numberInt" : "1"}, {"$numberInt" : "0"}, {"$numberInt" : "1"}, + {"$numberInt" : "0"}, {"$numberInt" : "1"}, {"$numberInt" : "0"}, {"$numberInt" : "1"}, + {"$numberInt" : "0"}, {"$numberInt" : "1"}, {"$numberInt" : "0"}, {"$numberInt" : "1"}, + {"$numberInt" : "0"}, {"$numberInt" : "1"}, {"$numberInt" : "0"}, {"$numberInt" : "1"}, + {"$numberInt" : "0"}, {"$numberInt" : "1"}, {"$numberInt" : "0"}, {"$numberInt" : "1"}, + {"$numberInt" : "0"}, {"$numberInt" : "1"}, {"$numberInt" : "0"}, {"$numberInt" : "1"}, + {"$numberInt" : "0"}, {"$numberInt" : "1"}, {"$numberInt" : "0"}, {"$numberInt" : "1"}, + {"$numberInt" : "0"}, {"$numberInt" : "1"}, {"$numberInt" : "0"}, {"$numberInt" : "1"}, + {"$numberInt" : "0"}, {"$numberInt" : "1"}, {"$numberInt" : "0"}, {"$numberInt" : "1"}, + {"$numberInt" : "0"}, {"$numberInt" : "1"}, {"$numberInt" : "0"}, {"$numberInt" : "1"}, + {"$numberInt" : "0"}, {"$numberInt" : "1"}, {"$numberInt" : "0"}, {"$numberInt" : "1"}, + {"$numberInt" : "0"}, {"$numberInt" : "1"}, {"$numberInt" : "0"}, {"$numberInt" : "1"}, + {"$numberInt" : "0"}, {"$numberInt" : "1"}, {"$numberInt" : "0"}, {"$numberInt" : "1"}, + {"$numberInt" : "0"}, {"$numberInt" : "1"}, {"$numberInt" : "0"}, {"$numberInt" : "1"}, + {"$numberInt" : "0"}, {"$numberInt" : "1"}, {"$numberInt" : "0"}, {"$numberInt" : "1"}, + {"$numberInt" : "0"}, {"$numberInt" : "0"}, {"$numberInt" : "0"}, {"$numberInt" : "1"}, + {"$numberInt" : "0"}, {"$numberInt" : "0"}, {"$numberInt" : "1"}, {"$numberInt" : "0"}, + {"$numberInt" : "0"}, {"$numberInt" : "0"}, {"$numberInt" : "1"}, {"$numberInt" : "1"}, + {"$numberInt" : "0"}, {"$numberInt" : "1"}, {"$numberInt" : "0"}, {"$numberInt" : "0"}, + {"$numberInt" : "0"}, {"$numberInt" : "1"}, {"$numberInt" : "0"}, {"$numberInt" : "1"}, + {"$numberInt" : "0"}, {"$numberInt" : "1"}, {"$numberInt" : "0"}, {"$numberInt" : "1"}, + {"$numberInt" : "0"}, {"$numberInt" : "1"}, {"$numberInt" : "0"} + ] + })); + + ASSERT (bson_iter_init_find (&iter, &converted, "array")); + ASSERT (BSON_ITER_HOLDS_ARRAY (&iter)); + ASSERT (bson_iter_recurse (&iter, &iter)); + ASSERT (BSON_APPEND_VECTOR_PACKED_BIT_FROM_ARRAY (&doc, "round_trip", &iter, NULL)); + bson_destroy (&converted); + } + + // The original small vector and round_trip small vector must be identical + { + bson_iter_t a, b; + ASSERT (bson_iter_init_find (&a, &doc, "vector")); + ASSERT (bson_iter_init_find (&b, &doc, "round_trip")); + ASSERT (bson_iter_binary_equal (&a, &b)); + } + + // Try the same round trip conversion with our longer vector + // (Note that BSON arrays special-case keys below "1000") + { + bson_t converted = BSON_INITIALIZER; + bson_iter_t iter; + ASSERT (bson_iter_init_find (&iter, &doc, "longer_vector")); + ASSERT (BSON_APPEND_ARRAY_FROM_VECTOR (&converted, "array", &iter)); + ASSERT (bson_iter_init_find (&iter, &converted, "array")); + ASSERT (BSON_ITER_HOLDS_ARRAY (&iter)); + ASSERT (bson_iter_recurse (&iter, &iter)); + ASSERT (BSON_APPEND_VECTOR_PACKED_BIT_FROM_ARRAY (&doc, "longer_round_trip", &iter, NULL)); + bson_destroy (&converted); + } + { + bson_iter_t a, b; + ASSERT (bson_iter_init_find (&a, &doc, "longer_vector")); + ASSERT (bson_iter_init_find (&b, &doc, "longer_round_trip")); + ASSERT (bson_iter_binary_equal (&a, &b)); + } + + // Padding bits will be initialized to zero when a packed_bit vector is first allocated by + // bson_append_vector_packed_bit_uninit + { + // Set the uninitialized part of 'doc' to a known value + static const uint32_t reserve_len = 512; + memset (bson_reserve_buffer (&doc, doc.len + reserve_len) + doc.len, 0xdd, reserve_len); + + bson_vector_packed_bit_view_t view; + ASSERT (BSON_APPEND_VECTOR_PACKED_BIT_UNINIT (&doc, "padding_init_test", 12, &view)); + ASSERT (bson_vector_packed_bit_view_length_bytes (view) == 2); + ASSERT (bson_vector_packed_bit_view_padding (view) == 4); + + // BSON validity only requires the low 4 bits to be zero, but the entire last + // byte will be zeroed by our implementation. + uint8_t bytes[2]; + ASSERT (bson_vector_packed_bit_view_read_packed (view, bytes, sizeof bytes, 0)); + ASSERT_CMPUINT ((unsigned) bytes[0], ==, 0xdd); + ASSERT_CMPUINT ((unsigned) bytes[1], ==, 0x00); + } + + // Padding bits can't be forcibly given nonzero values using bson_vector_packed_bit_view_write_packed + { + bson_vector_packed_bit_view_t view; + ASSERT (BSON_APPEND_VECTOR_PACKED_BIT_UNINIT (&doc, "padding_mask_test", 13, &view)); + ASSERT (bson_vector_packed_bit_view_length_bytes (view) == 2); + ASSERT (bson_vector_packed_bit_view_padding (view) == 3); + + uint8_t bytes[2] = {0xff, 0xff}; + ASSERT (bson_vector_packed_bit_view_write_packed (view, bytes, sizeof bytes, 0)); + ASSERT (bson_vector_packed_bit_view_read_packed (view, bytes, sizeof bytes, 0)); + ASSERT_CMPUINT ((unsigned) bytes[0], ==, 0xff); + ASSERT_CMPUINT ((unsigned) bytes[1], ==, 0xf8); + } + + bson_destroy (&doc); +} + +// Note: The effective MAX_TESTED_VECTOR_LENGTH is limited to 2047 on Windows due to RAND_MAX==0x7fff +#define MAX_TESTED_VECTOR_LENGTH 10000 +#define FUZZ_TEST_ITERS 5000 + +static void +test_bson_vector_view_api_fuzz_int8 (void) +{ + size_t current_length = 0; + bson_t vector_doc = BSON_INITIALIZER; + int8_t *expected_elements = bson_malloc (MAX_TESTED_VECTOR_LENGTH * sizeof *expected_elements); + int8_t *actual_elements = bson_malloc (MAX_TESTED_VECTOR_LENGTH * sizeof *actual_elements); + for (int fuzz_iter = 0; fuzz_iter < FUZZ_TEST_ITERS; fuzz_iter++) { + unsigned r = (unsigned) rand (); + unsigned r_operation = r & 0xFu; + size_t r_param = r >> 4; + + if (current_length == 0 || r_operation == 15) { + // Resize and fill + size_t new_length = (size_t) r_param % MAX_TESTED_VECTOR_LENGTH; + bson_reinit (&vector_doc); + bson_vector_int8_view_t view; + ASSERT (BSON_APPEND_VECTOR_INT8_UNINIT (&vector_doc, "vector", new_length, &view)); + for (size_t i = 0; i < new_length; i++) { + expected_elements[i] = (int8_t) (uint8_t) rand (); + } + ASSERT (bson_vector_int8_view_write (view, expected_elements, new_length, 0)); + current_length = new_length; + + } else if (r_operation < 7) { + // Partial write + size_t element_count = r_param % current_length; + size_t offset = (size_t) rand () % (current_length - element_count); + for (size_t i = 0; i < element_count; i++) { + expected_elements[offset + i] = (int8_t) (uint8_t) rand (); + } + bson_vector_int8_view_t view; + bson_iter_t iter; + ASSERT (bson_iter_init_find (&iter, &vector_doc, "vector")); + ASSERT (bson_vector_int8_view_from_iter (&view, &iter)); + ASSERT (bson_vector_int8_view_write (view, expected_elements + offset, element_count, offset)); + + } else { + // Partial read + size_t element_count = r_param % current_length; + size_t offset = (size_t) rand () % (current_length - element_count); + bson_vector_int8_const_view_t view; + bson_iter_t iter; + ASSERT (bson_iter_init_find (&iter, &vector_doc, "vector")); + ASSERT (bson_vector_int8_const_view_from_iter (&view, &iter)); + ASSERT (bson_vector_int8_const_view_read (view, actual_elements, element_count, offset)); + ASSERT_MEMCMP (actual_elements, expected_elements + offset, element_count * sizeof *actual_elements); + } + } + bson_destroy (&vector_doc); + bson_free (expected_elements); + bson_free (actual_elements); +} + +static void +test_bson_vector_view_api_fuzz_float32 (void) +{ + size_t current_length = 0; + bson_t vector_doc = BSON_INITIALIZER; + float *expected_elements = bson_malloc (MAX_TESTED_VECTOR_LENGTH * sizeof *expected_elements); + float *actual_elements = bson_malloc (MAX_TESTED_VECTOR_LENGTH * sizeof *actual_elements); + for (int fuzz_iter = 0; fuzz_iter < FUZZ_TEST_ITERS; fuzz_iter++) { + unsigned r = (unsigned) rand (); + unsigned r_operation = r & 0xFu; + size_t r_param = r >> 4; + + if (current_length == 0 || r_operation == 15) { + // Resize and fill + size_t new_length = (size_t) r_param % MAX_TESTED_VECTOR_LENGTH; + bson_reinit (&vector_doc); + bson_vector_float32_view_t view; + ASSERT (BSON_APPEND_VECTOR_FLOAT32_UNINIT (&vector_doc, "vector", new_length, &view)); + for (size_t i = 0; i < new_length; i++) { + expected_elements[i] = (float) rand (); + } + ASSERT (bson_vector_float32_view_write (view, expected_elements, new_length, 0)); + current_length = new_length; + + } else if (r_operation < 7) { + // Partial write + size_t element_count = r_param % current_length; + size_t offset = (size_t) rand () % (current_length - element_count); + for (size_t i = 0; i < element_count; i++) { + expected_elements[offset + i] = (float) rand (); + } + bson_vector_float32_view_t view; + bson_iter_t iter; + ASSERT (bson_iter_init_find (&iter, &vector_doc, "vector")); + ASSERT (bson_vector_float32_view_from_iter (&view, &iter)); + ASSERT (bson_vector_float32_view_write (view, expected_elements + offset, element_count, offset)); + + } else { + // Partial read + size_t element_count = r_param % current_length; + size_t offset = (size_t) rand () % (current_length - element_count); + bson_vector_float32_const_view_t view; + bson_iter_t iter; + ASSERT (bson_iter_init_find (&iter, &vector_doc, "vector")); + ASSERT (bson_vector_float32_const_view_from_iter (&view, &iter)); + ASSERT (bson_vector_float32_const_view_read (view, actual_elements, element_count, offset)); + ASSERT_MEMCMP (actual_elements, expected_elements + offset, element_count * sizeof *actual_elements); + } + } + bson_destroy (&vector_doc); + bson_free (expected_elements); + bson_free (actual_elements); +} + +static void +test_bson_vector_view_api_fuzz_packed_bit (void) +{ + size_t current_length = 0; + bson_t vector_doc = BSON_INITIALIZER; + bool *expected_elements = bson_malloc (MAX_TESTED_VECTOR_LENGTH * sizeof *expected_elements); + bool *actual_elements = bson_malloc (MAX_TESTED_VECTOR_LENGTH * sizeof *actual_elements); + uint8_t *packed_buffer = bson_malloc ((MAX_TESTED_VECTOR_LENGTH + 7) / 8); + for (int fuzz_iter = 0; fuzz_iter < FUZZ_TEST_ITERS; fuzz_iter++) { + unsigned r = (unsigned) rand (); + unsigned r_operation = r & 0xFu; + size_t r_param = r >> 4; + + if (current_length == 0 || r_operation == 15) { + // Resize and fill from unpacked bool source + size_t new_length = (size_t) r_param % MAX_TESTED_VECTOR_LENGTH; + bson_reinit (&vector_doc); + bson_vector_packed_bit_view_t view; + ASSERT (BSON_APPEND_VECTOR_PACKED_BIT_UNINIT (&vector_doc, "vector", new_length, &view)); + for (size_t i = 0; i < new_length; i++) { + expected_elements[i] = ((unsigned) rand () & 1u) != 0u; + } + ASSERT (bson_vector_packed_bit_view_pack_bool (view, expected_elements, new_length, 0)); + current_length = new_length; + + } else if (r_operation < 7) { + // Partial write + if (r_operation & 1) { + // Partial write from unpacked bool source + size_t element_count = r_param % current_length; + size_t offset = (size_t) rand () % (current_length - element_count); + for (size_t i = 0; i < element_count; i++) { + expected_elements[offset + i] = ((unsigned) rand () & 1u) != 0u; + } + bson_vector_packed_bit_view_t view; + bson_iter_t iter; + ASSERT (bson_iter_init_find (&iter, &vector_doc, "vector")); + ASSERT (bson_vector_packed_bit_view_from_iter (&view, &iter)); + ASSERT (bson_vector_packed_bit_view_length (view) == current_length); + ASSERT (bson_vector_packed_bit_view_pack_bool (view, expected_elements + offset, element_count, offset)); + } else { + // Partial write of packed bytes + size_t current_length_bytes = (current_length + 7) / 8; + size_t byte_count = r_param % current_length_bytes; + size_t byte_offset = (size_t) rand () % (current_length_bytes - byte_count); + for (size_t i = 0; i < byte_count; i++) { + uint8_t packed_byte = (uint8_t) rand (); + packed_buffer[i] = packed_byte; + for (unsigned bit = 0; bit < 8; bit++) { + expected_elements[(byte_offset + i) * 8 + bit] = (packed_byte & (0x80 >> bit)) != 0; + } + } + bson_vector_packed_bit_view_t view; + bson_iter_t iter; + ASSERT (bson_iter_init_find (&iter, &vector_doc, "vector")); + ASSERT (bson_vector_packed_bit_view_from_iter (&view, &iter)); + ASSERT (bson_vector_packed_bit_view_length (view) == current_length); + ASSERT (bson_vector_packed_bit_view_length_bytes (view) == current_length_bytes); + ASSERT (bson_vector_packed_bit_view_write_packed (view, packed_buffer, byte_count, byte_offset)); + } + } else { + // Partial read + if (r_operation & 1) { + // Partial read to unpacked bool destination + size_t element_count = r_param % current_length; + size_t offset = (size_t) rand () % (current_length - element_count); + bson_vector_packed_bit_const_view_t view; + bson_iter_t iter; + ASSERT (bson_iter_init_find (&iter, &vector_doc, "vector")); + ASSERT (bson_vector_packed_bit_const_view_from_iter (&view, &iter)); + ASSERT (bson_vector_packed_bit_const_view_length (view) == current_length); + ASSERT (bson_vector_packed_bit_const_view_unpack_bool (view, actual_elements, element_count, offset)); + ASSERT_MEMCMP (actual_elements, expected_elements + offset, element_count * sizeof *actual_elements); + } else { + // Partial read of packed bytes + size_t current_length_bytes = (current_length + 7) / 8; + size_t byte_count = r_param % current_length_bytes; + size_t byte_offset = (size_t) rand () % (current_length_bytes - byte_count); + bson_vector_packed_bit_const_view_t view; + bson_iter_t iter; + ASSERT (bson_iter_init_find (&iter, &vector_doc, "vector")); + ASSERT (bson_vector_packed_bit_const_view_from_iter (&view, &iter)); + ASSERT (bson_vector_packed_bit_const_view_length (view) == current_length); + ASSERT (bson_vector_packed_bit_const_view_length_bytes (view) == current_length_bytes); + ASSERT (bson_vector_packed_bit_const_view_read_packed (view, packed_buffer, byte_count, byte_offset)); + for (size_t i = 0; i < byte_count; i++) { + uint8_t packed_byte = packed_buffer[i]; + for (unsigned bit = 0; bit < 8; bit++) { + ASSERT (expected_elements[(byte_offset + i) * 8 + bit] == ((packed_byte & (0x80 >> bit)) != 0)); + } + } + } + } + } + bson_destroy (&vector_doc); + bson_free (expected_elements); + bson_free (actual_elements); + bson_free (packed_buffer); +} + +static void +test_bson_vector_example_int8_const_view (void) +{ + // setup: construct a sample document + bson_t doc = BSON_INITIALIZER; + { + static const int8_t values[] = {12, 34, -56}; + bson_vector_int8_view_t view; + ASSERT (BSON_APPEND_VECTOR_INT8_UNINIT (&doc, "vector", sizeof values / sizeof values[0], &view)); + ASSERT (bson_vector_int8_view_write (view, values, sizeof values / sizeof values[0], 0)); + } + + // bson_vector_int8_const_view_t.rst + // Edits: + // - Added test_suite_debug_output() test. + // - Added unnecessary zero initializer to work around false positive compiler warning. + // (same as in bson_array_builder_append_vector_int8_elements) + { + bson_iter_t iter; + bson_vector_int8_const_view_t view; + + if (bson_iter_init_find (&iter, &doc, "vector") && bson_vector_int8_const_view_from_iter (&view, &iter)) { + size_t length = bson_vector_int8_const_view_length (view); + if (test_suite_debug_output ()) { + printf ("Elements in 'vector':\n"); + } + for (size_t i = 0; i < length; i++) { + int8_t element = 0; // Workaround + ASSERT (bson_vector_int8_const_view_read (view, &element, 1, i)); + if (test_suite_debug_output ()) { + printf (" [%d] = %d\n", (int) i, (int) element); + } + } + } + } + + bson_destroy (&doc); +} + +static void +test_bson_vector_example_int8_view (void) +{ + bson_t doc = BSON_INITIALIZER; + + // bson_vector_int8_view_t.rst + { + static const int8_t values[] = {1, 2, 3}; + const size_t values_count = sizeof values / sizeof values[0]; + + bson_vector_int8_view_t view; + ASSERT (BSON_APPEND_VECTOR_INT8_UNINIT (&doc, "vector", values_count, &view)); + ASSERT (bson_vector_int8_view_write (view, values, values_count, 0)); + } + + bson_destroy (&doc); +} + +static void +test_bson_vector_example_float32_const_view (void) +{ + // setup: construct a sample document + bson_t doc = BSON_INITIALIZER; + { + static const float values[] = {5.0f, -1e10f, INFINITY, NAN, -1.0f}; + bson_vector_float32_view_t view; + ASSERT (BSON_APPEND_VECTOR_FLOAT32_UNINIT (&doc, "vector", sizeof values / sizeof values[0], &view)); + ASSERT (bson_vector_float32_view_write (view, values, sizeof values / sizeof values[0], 0)); + } + + // bson_vector_float32_const_view_t.rst + // Edits: + // - Added test_suite_debug_output() test. + { + bson_iter_t iter; + bson_vector_float32_const_view_t view; + + if (bson_iter_init_find (&iter, &doc, "vector") && bson_vector_float32_const_view_from_iter (&view, &iter)) { + size_t length = bson_vector_float32_const_view_length (view); + if (test_suite_debug_output ()) { + printf ("Elements in 'vector':\n"); + } + for (size_t i = 0; i < length; i++) { + float element; + ASSERT (bson_vector_float32_const_view_read (view, &element, 1, i)); + if (test_suite_debug_output ()) { + printf (" [%d] = %f\n", (int) i, element); + } + } + } + } + + bson_destroy (&doc); +} + +static void +test_bson_vector_example_float32_view (void) +{ + bson_t doc = BSON_INITIALIZER; + + // bson_vector_float32_view_t.rst + { + static const float values[] = {1.0f, 2.0f, 3.0f}; + const size_t values_count = sizeof values / sizeof values[0]; + + bson_vector_float32_view_t view; + ASSERT (BSON_APPEND_VECTOR_FLOAT32_UNINIT (&doc, "vector", values_count, &view)); + ASSERT (bson_vector_float32_view_write (view, values, values_count, 0)); + } + + bson_destroy (&doc); +} + +static void +test_bson_vector_example_packed_bit_const_view (void) +{ + // setup: construct a sample document + bson_t doc = BSON_INITIALIZER; + { + static const bool values[] = {true, false, true, true, false, true, false, true, true, false}; + bson_vector_packed_bit_view_t view; + ASSERT (BSON_APPEND_VECTOR_PACKED_BIT_UNINIT (&doc, "vector", sizeof values / sizeof values[0], &view)); + ASSERT (bson_vector_packed_bit_view_pack_bool (view, values, sizeof values / sizeof values[0], 0)); + } + + // bson_vector_packed_bit_const_view_t.rst + // Edits: + // - Added test_suite_debug_output() test. + // - Added unnecessary zero initializer to work around false positive compiler warning. + // (same as in bson_array_builder_append_vector_int8_elements) + { + bson_iter_t iter; + bson_vector_packed_bit_const_view_t view; + + if (bson_iter_init_find (&iter, &doc, "vector") && bson_vector_packed_bit_const_view_from_iter (&view, &iter)) { + size_t length = bson_vector_packed_bit_const_view_length (view); + size_t length_bytes = bson_vector_packed_bit_const_view_length_bytes (view); + size_t padding = bson_vector_packed_bit_const_view_padding (view); + + if (test_suite_debug_output ()) { + printf ("Elements in 'vector':\n"); + } + for (size_t i = 0; i < length; i++) { + bool element; + ASSERT (bson_vector_packed_bit_const_view_unpack_bool (view, &element, 1, i)); + if (test_suite_debug_output ()) { + printf (" elements[%d] = %d\n", (int) i, (int) element); + } + } + + if (test_suite_debug_output ()) { + printf ("Bytes in 'vector': (%d bits unused)\n", (int) padding); + } + for (size_t i = 0; i < length_bytes; i++) { + uint8_t packed_byte = 0; // Workaround + ASSERT (bson_vector_packed_bit_const_view_read_packed (view, &packed_byte, 1, i)); + if (test_suite_debug_output ()) { + printf (" bytes[%d] = 0x%02x\n", (int) i, (unsigned) packed_byte); + } + } + } + } + + bson_destroy (&doc); +} + +static void +test_bson_vector_example_packed_bit_view (void) +{ + bson_t doc = BSON_INITIALIZER; + + // bson_vector_packed_bit_view_t.rst + { + // Fill a new vector with individual boolean elements + { + static const bool bool_values[] = {true, false, true, true, false}; + const size_t bool_values_count = sizeof bool_values / sizeof bool_values[0]; + + bson_vector_packed_bit_view_t view; + ASSERT (BSON_APPEND_VECTOR_PACKED_BIT_UNINIT (&doc, "from_bool", bool_values_count, &view)); + ASSERT (bson_vector_packed_bit_view_pack_bool (view, bool_values, bool_values_count, 0)); + } + + // Fill another new vector with packed bytes + { + static const uint8_t packed_bytes[] = {0xb0}; + const size_t unused_bits_count = 3; + const size_t packed_values_count = sizeof packed_bytes * 8 - unused_bits_count; + + bson_vector_packed_bit_view_t view; + ASSERT (BSON_APPEND_VECTOR_PACKED_BIT_UNINIT (&doc, "from_packed", packed_values_count, &view)); + ASSERT (bson_vector_packed_bit_view_write_packed (view, packed_bytes, sizeof packed_bytes, 0)); + } + + // Compare both vectors. They match exactly. + { + bson_iter_t from_bool_iter, from_packed_iter; + ASSERT (bson_iter_init_find (&from_bool_iter, &doc, "from_bool")); + ASSERT (bson_iter_init_find (&from_packed_iter, &doc, "from_packed")); + ASSERT (bson_iter_binary_equal (&from_bool_iter, &from_packed_iter)); + } + } + + bson_destroy (&doc); +} + +// Shared edge case tests that apply to all reader/writer functions. +#define TEST_BSON_VECTOR_RW(_expected, _view, _v, _count, _offset, _read, _write) \ + if (true) { \ + ASSERT ((_expected) == (_write) ((_view), (_v), (_count), (_offset))); \ + ASSERT ((_expected) == (_read) ((_view), (_v), (_count), (_offset))); \ + } else \ + ((void) 0) + +#define TEST_BSON_VECTOR_EDGE_CASES_RW_COMMON(_view, _alloc_size, _v, _v_size, _read, _write) \ + if (true) { \ + TEST_BSON_VECTOR_RW (false, (_view), (_v), (_alloc_size) + 1u, 0, (_read), (_write)); \ + TEST_BSON_VECTOR_RW (true, (_view), (_v), (_v_size), (_alloc_size) - (_v_size), (_read), (_write)); \ + TEST_BSON_VECTOR_RW (false, (_view), (_v), (_v_size), (_alloc_size) - (_v_size) + 1u, (_read), (_write)); \ + TEST_BSON_VECTOR_RW (false, (_view), (_v), (_v_size) + 1u, (_alloc_size) - (_v_size), (_read), (_write)); \ + TEST_BSON_VECTOR_RW (false, (_view), (_v), SIZE_MAX, (_alloc_size) - (_v_size), (_read), (_write)); \ + TEST_BSON_VECTOR_RW (false, (_view), (_v), SIZE_MAX, (_alloc_size) - (_v_size) + 1u, (_read), (_write)); \ + TEST_BSON_VECTOR_RW (true, (_view), (_v), (_v_size), 0, (_read), (_write)); \ + } else \ + ((void) 0) + +static void +test_bson_vector_edge_cases_int8 (void) +{ + size_t max_representable_elements = (size_t) UINT32_MAX - BSON_VECTOR_HEADER_LEN; + + // Test binary_data_length (uint32_t) edge cases, without any allocation. + { + ASSERT_CMPUINT32 (bson_vector_int8_binary_data_length (max_representable_elements - 1u), ==, UINT32_MAX - 1u); + ASSERT_CMPUINT32 (bson_vector_int8_binary_data_length (max_representable_elements), ==, UINT32_MAX); + ASSERT_CMPUINT32 (bson_vector_int8_binary_data_length (max_representable_elements + 1u), ==, 0); + } + + // Needs little real memory because most bytes are never accessed, + // but we should require a virtual address space larger than 32 bits. +#if BSON_WORD_SIZE > 32 + + size_t expected_bson_overhead = + 5 /* empty bson document */ + 3 /* "v" element header */ + 5 /* binary item header */; + size_t max_alloc_elements = (size_t) BSON_MAX_SIZE - expected_bson_overhead - BSON_VECTOR_HEADER_LEN; + + bson_t doc = BSON_INITIALIZER; + bson_vector_int8_view_t view; + + // Test allocation (BSON_MAX_SIZE + uint32_t) edge cases. + { + ASSERT (!BSON_APPEND_VECTOR_INT8_UNINIT (&doc, "v", max_representable_elements, &view)); + ASSERT (!BSON_APPEND_VECTOR_INT8_UNINIT (&doc, "v", max_representable_elements + 1u, &view)); + ASSERT (!BSON_APPEND_VECTOR_INT8_UNINIT (&doc, "v", max_alloc_elements + 1u, &view)); + ASSERT (BSON_APPEND_VECTOR_INT8_UNINIT (&doc, "v", max_alloc_elements, &view)); + } + + // Test some read and write boundaries. + { + size_t values_size = 100; + int8_t *values = bson_malloc0 (values_size * sizeof *values); + TEST_BSON_VECTOR_EDGE_CASES_RW_COMMON ( + view, max_alloc_elements, values, values_size, bson_vector_int8_view_read, bson_vector_int8_view_write); + bson_free (values); + } + + bson_destroy (&doc); +#endif // BSON_WORD_SIZE > 32 +} + +static void +test_bson_vector_edge_cases_float32 (void) +{ + size_t max_representable_elements = ((size_t) UINT32_MAX - BSON_VECTOR_HEADER_LEN) / sizeof (float); + + // Test binary_data_length (uint32_t) edge cases, without any allocation. + // Note that the longest possible multiple of a complete element is 1 byte short of UINT32_MAX. + { + ASSERT_CMPUINT32 ( + bson_vector_float32_binary_data_length (max_representable_elements - 1u), ==, UINT32_MAX - 1u - 4u); + ASSERT_CMPUINT32 (bson_vector_float32_binary_data_length (max_representable_elements), ==, UINT32_MAX - 1u); + ASSERT_CMPUINT32 (bson_vector_float32_binary_data_length (max_representable_elements + 1u), ==, 0); + } + + // Needs little real memory because most bytes are never accessed, + // but we should require a virtual address space larger than 32 bits. +#if BSON_WORD_SIZE > 32 + + size_t expected_bson_overhead = + 5 /* empty bson document */ + 3 /* "v" element header */ + 5 /* binary item header */; + size_t max_alloc_elements = + ((size_t) BSON_MAX_SIZE - expected_bson_overhead - BSON_VECTOR_HEADER_LEN) / sizeof (float); + + bson_t doc = BSON_INITIALIZER; + bson_vector_float32_view_t view; + + // Test allocation (BSON_MAX_SIZE + uint32_t) edge cases. + { + ASSERT (!BSON_APPEND_VECTOR_FLOAT32_UNINIT (&doc, "v", max_representable_elements, &view)); + ASSERT (!BSON_APPEND_VECTOR_FLOAT32_UNINIT (&doc, "v", max_representable_elements + 1u, &view)); + ASSERT (!BSON_APPEND_VECTOR_FLOAT32_UNINIT (&doc, "v", max_alloc_elements + 1u, &view)); + ASSERT (BSON_APPEND_VECTOR_FLOAT32_UNINIT (&doc, "v", max_alloc_elements, &view)); + } + + // Test some read and write boundaries. + { + size_t values_size = 100; + float *values = bson_malloc0 (values_size * sizeof *values); + TEST_BSON_VECTOR_EDGE_CASES_RW_COMMON ( + view, max_alloc_elements, values, values_size, bson_vector_float32_view_read, bson_vector_float32_view_write); + bson_free (values); + } + + bson_destroy (&doc); +#endif // BSON_WORD_SIZE > 32 +} + +static void +test_bson_vector_edge_cases_packed_bit (void) +{ + size_t max_representable_elements = ((size_t) UINT32_MAX - BSON_VECTOR_HEADER_LEN) * 8u; + + // Test binary_data_length (uint32_t) edge cases, without any allocation. + { + ASSERT_CMPUINT32 ( + bson_vector_packed_bit_binary_data_length (max_representable_elements - 8u), ==, UINT32_MAX - 1u); + ASSERT_CMPUINT32 (bson_vector_packed_bit_binary_data_length (max_representable_elements - 7u), ==, UINT32_MAX); + ASSERT_CMPUINT32 (bson_vector_packed_bit_binary_data_length (max_representable_elements), ==, UINT32_MAX); + ASSERT_CMPUINT32 (bson_vector_packed_bit_binary_data_length (max_representable_elements + 1u), ==, 0); + } + + // Needs little real memory because most bytes are never accessed, + // but we should require a virtual address space larger than 32 bits. +#if BSON_WORD_SIZE > 32 + + size_t expected_bson_overhead = + 5 /* empty bson document */ + 3 /* "v" element header */ + 5 /* binary item header */; + size_t max_alloc_bytes = (size_t) BSON_MAX_SIZE - expected_bson_overhead - BSON_VECTOR_HEADER_LEN; + size_t max_alloc_elements = max_alloc_bytes * 8u; + + bson_t doc = BSON_INITIALIZER; + bson_vector_packed_bit_view_t view; + + // Test allocation (BSON_MAX_SIZE + uint32_t) edge cases. + { + ASSERT (!BSON_APPEND_VECTOR_PACKED_BIT_UNINIT (&doc, "v", max_representable_elements, &view)); + ASSERT (!BSON_APPEND_VECTOR_PACKED_BIT_UNINIT (&doc, "v", max_representable_elements + 1u, &view)); + ASSERT (!BSON_APPEND_VECTOR_PACKED_BIT_UNINIT (&doc, "v", max_alloc_elements + 1u, &view)); + ASSERT (BSON_APPEND_VECTOR_PACKED_BIT_UNINIT (&doc, "v", max_alloc_elements, &view)); + } + + // Test pack and unpack boundaries with the same tests used for read/write of non-packed element types. + // Only tests one length, but it's chosen to be greater than 8 and not a multiple of 8. + { + size_t values_size = 190; + bool *values = bson_malloc0 (values_size * sizeof *values); + TEST_BSON_VECTOR_EDGE_CASES_RW_COMMON (view, + max_alloc_elements, + values, + values_size, + bson_vector_packed_bit_view_unpack_bool, + bson_vector_packed_bit_view_pack_bool); + bson_free (values); + } + + // Test read and write boundaries on packed bytes. + { + size_t packed_size = 50; + uint8_t *packed = bson_malloc0 (packed_size); + TEST_BSON_VECTOR_EDGE_CASES_RW_COMMON (view, + max_alloc_bytes, + packed, + packed_size, + bson_vector_packed_bit_view_read_packed, + bson_vector_packed_bit_view_write_packed); + bson_free (packed); + } + + bson_destroy (&doc); +#endif // BSON_WORD_SIZE > 32 +} + +void +test_bson_vector_install (TestSuite *suite) +{ + install_json_test_suite_with_check (suite, BSON_JSON_DIR, "bson_binary_vector", test_bson_vector_json_cb); + + TestSuite_Add (suite, "/bson_binary_vector/view_api/usage/int8", test_bson_vector_view_api_usage_int8); + TestSuite_Add (suite, "/bson_binary_vector/view_api/usage/float32", test_bson_vector_view_api_usage_float32); + TestSuite_Add (suite, "/bson_binary_vector/view_api/usage/packed_bit", test_bson_vector_view_api_usage_packed_bit); + + TestSuite_Add (suite, "/bson_binary_vector/view_api/fuzz/int8", test_bson_vector_view_api_fuzz_int8); + TestSuite_Add (suite, "/bson_binary_vector/view_api/fuzz/float32", test_bson_vector_view_api_fuzz_float32); + TestSuite_Add (suite, "/bson_binary_vector/view_api/fuzz/packed_bit", test_bson_vector_view_api_fuzz_packed_bit); + + TestSuite_Add (suite, "/bson_binary_vector/example/int8_const_view", test_bson_vector_example_int8_const_view); + TestSuite_Add (suite, "/bson_binary_vector/example/int8_view", test_bson_vector_example_int8_view); + TestSuite_Add (suite, "/bson_binary_vector/example/float32_const_view", test_bson_vector_example_float32_const_view); + TestSuite_Add (suite, "/bson_binary_vector/example/float32_view", test_bson_vector_example_float32_view); + TestSuite_Add ( + suite, "/bson_binary_vector/example/packed_bit_const_view", test_bson_vector_example_packed_bit_const_view); + TestSuite_Add (suite, "/bson_binary_vector/example/packed_bit_view", test_bson_vector_example_packed_bit_view); + + TestSuite_Add (suite, "/bson_binary_vector/edge_cases/int8", test_bson_vector_edge_cases_int8); + TestSuite_Add (suite, "/bson_binary_vector/edge_cases/float32", test_bson_vector_edge_cases_float32); + TestSuite_Add (suite, "/bson_binary_vector/edge_cases/packed_bit", test_bson_vector_edge_cases_packed_bit); +} diff --git a/src/libmongoc/CMakeLists.txt b/src/libmongoc/CMakeLists.txt index 35c75e545f6..6ac4ac583bb 100644 --- a/src/libmongoc/CMakeLists.txt +++ b/src/libmongoc/CMakeLists.txt @@ -1036,13 +1036,13 @@ set (test-libmongoc-sources ${mongo-c-driver_SOURCE_DIR}/src/libbson/tests/corpus-test.h ${mongo-c-driver_SOURCE_DIR}/src/libbson/tests/test-atomic.c ${mongo-c-driver_SOURCE_DIR}/src/libbson/tests/test-b64.c - ${mongo-c-driver_SOURCE_DIR}/src/libbson/tests/test-bson.c ${mongo-c-driver_SOURCE_DIR}/src/libbson/tests/test-bcon-basic.c ${mongo-c-driver_SOURCE_DIR}/src/libbson/tests/test-bcon-extract.c ${mongo-c-driver_SOURCE_DIR}/src/libbson/tests/test-bson-cmp.c ${mongo-c-driver_SOURCE_DIR}/src/libbson/tests/test-bson-corpus.c ${mongo-c-driver_SOURCE_DIR}/src/libbson/tests/test-bson-error.c ${mongo-c-driver_SOURCE_DIR}/src/libbson/tests/test-bson-sync.c + ${mongo-c-driver_SOURCE_DIR}/src/libbson/tests/test-bson-vector.c ${mongo-c-driver_SOURCE_DIR}/src/libbson/tests/test-bson-version.c ${mongo-c-driver_SOURCE_DIR}/src/libbson/tests/test-bson.c ${mongo-c-driver_SOURCE_DIR}/src/libbson/tests/test-clock.c diff --git a/src/libmongoc/tests/TestSuite.h b/src/libmongoc/tests/TestSuite.h index 635f4e3ff24..4d9a4ab3fb0 100644 --- a/src/libmongoc/tests/TestSuite.h +++ b/src/libmongoc/tests/TestSuite.h @@ -120,6 +120,20 @@ bson_open (const char *filename, int flags, ...) } else \ ((void) 0) +#define MONGOC_STDERR_HEXDUMP(pointer, length) \ + if (1) { \ + const uint8_t *_pointer = (const uint8_t *) (pointer); \ + const size_t _length = (length); \ + fflush (stdout); \ + putc ('<', stderr); \ + for (size_t _i = 0; _i < _length; _i++) { \ + fprintf (stderr, "%s%02x", _i ? " " : "", _pointer[_i]); \ + } \ + putc ('>', stderr); \ + fflush (stderr); \ + } else \ + ((void) 0) + #define ASSERT(Cond) \ do { \ if (!(Cond)) { \ @@ -260,15 +274,23 @@ _test_error (const char *format, ...) BSON_GNUC_PRINTF (1, 2); #define ASSERT_CMPDOUBLE(a, eq, b) ASSERT_CMPINT_HELPER (a, eq, b, "f", double) #define ASSERT_CMPVOID(a, eq, b) ASSERT_CMPINT_HELPER (a, eq, b, "p", void *) -#define ASSERT_MEMCMP(a, b, n) \ - do { \ - if (0 != memcmp (a, b, n)) { \ - MONGOC_STDERR_PRINTF ("Failed comparing %d bytes: \"%.*s\" != \"%.*s\"", n, n, (char *) a, n, (char *) b); \ - abort (); \ - } \ +#define ASSERT_MEMCMP(a, b, n) \ + do { \ + const void *_a = (a); \ + const void *_b = (b); \ + const size_t _n = (n); \ + if (0 != memcmp (_a, _b, _n)) { \ + MONGOC_STDERR_PRINTF ("FAIL\n\nAssert Failure: Expected an exact match of %" PRIu64 " bytes:\n ", \ + (uint64_t) _n); \ + MONGOC_STDERR_HEXDUMP (_a, _n); \ + fprintf (stderr, " !=\n "); \ + MONGOC_STDERR_HEXDUMP (_b, _n); \ + MONGOC_STDERR_PRINTF ("\n %s:%d %s()\n", __FILE__, (int) (__LINE__), BSON_FUNC); \ + fflush (stderr); \ + abort (); \ + } \ } while (0) - #ifdef ASSERT_ALMOST_EQUAL #undef ASSERT_ALMOST_EQUAL #endif diff --git a/src/libmongoc/tests/test-libmongoc-main.c b/src/libmongoc/tests/test-libmongoc-main.c index fdee937cd8b..2d33639a83f 100644 --- a/src/libmongoc/tests/test-libmongoc-main.c +++ b/src/libmongoc/tests/test-libmongoc-main.c @@ -32,6 +32,7 @@ main (int argc, char *argv[]) TEST_INSTALL (test_bson_install); TEST_INSTALL (test_bson_sync_install); TEST_INSTALL (test_bson_version_install); + TEST_INSTALL (test_bson_vector_install); TEST_INSTALL (test_clock_install); TEST_INSTALL (test_decimal128_install); TEST_INSTALL (test_endian_install);