зеркало из https://github.com/Azure/azure-ulib-c.git
965 строки
53 KiB
C++
965 строки
53 KiB
C++
// Copyright (c) Microsoft. All rights reserved.
|
|
// Licensed under the MIT license.
|
|
// See LICENSE file in the project root for full license information.
|
|
|
|
#ifndef AZ_ULIB_USTREAM_BASE_H
|
|
#define AZ_ULIB_USTREAM_BASE_H
|
|
|
|
/**
|
|
* @file az_ulib_ustream_base.h
|
|
* @brief **uStream Interface Definition**
|
|
*
|
|
* This is the definition of a heterogeneous buffer that helps other modules in a system expose
|
|
* large amounts of data without using a large amount of memory. Modules in the system can
|
|
* expose their own data using this interface. To do that, the module shall implement the
|
|
* functions in the interface. This implementation shall follow the definition described in
|
|
* this file, which includes not only the prototype of the header, but the behavior as well.
|
|
* uStream defines a provider-consumer interface when:
|
|
* - <b>Provider</b> - is the module of code that handles data to be exposed. This module
|
|
* implements the ustream interface to expose the data to the consumer.
|
|
* - <b>Consumer</b> - is the module of code that will use the data exposed by the provider.
|
|
*
|
|
* The ustream shall have a clear separation between the internal content (provider domain)
|
|
* and what it exposes as external content (consumer domain). The ustream shall never expose
|
|
* the internal content (ex: providing a pointer to a internal memory position). All
|
|
* exposed content shall be copied from the internal data source to some given external
|
|
* memory. To do that in a clear way, the ustream shall always work with the concept of two
|
|
* buffers, the `data source` and the `local buffer`, adhering to the following definition:
|
|
* - <b>Data source</b> - is the place where the data is stored by the implementation of the
|
|
* ustream interface. The data source is in the provider domain, and it shall be
|
|
* protected, immutable, and non volatile. Consumers can read the data from the data
|
|
* source by the calling the az_ulib_ustream_read() API, which will copy a snapshot of
|
|
* the data to the provided external memory, called local buffer.
|
|
* - <b>Local buffer</b> - is the consumer domain buffer, where the az_ulib_ustream_read()
|
|
* API will copy the required bytes from the data source. The local buffer belongs to the
|
|
* consumer of this interface, which means that the consumer shall allocate and free
|
|
* (if necessary) this memory, and the content of the local buffer can be changed and
|
|
* released.
|
|
*
|
|
* <i><b>Example</b></i>
|
|
*
|
|
* A provider wants to create a ustream to expose data to the consumer. The provider will
|
|
* store the content in the HEAP, and will create a ustream from it, passing the ownership of
|
|
* the content to the ustream. Consumer will print the content of the ustream, using a
|
|
* local buffer of 1K. The following diagram represents this operation.
|
|
*
|
|
* @code
|
|
* +----------------+ +----------------+ +------------------+ +------------+
|
|
* | Provider | | Consumer | | ustream | | HEAP |
|
|
* +----------------+ +----------------+ +------------------+ +------------+
|
|
* | +-------------+ | |
|
|
* | | [Allocate on stack or heap] | |
|
|
* | | [In this example the stack] | |
|
|
* | | az_ulib_ustream ustream_instance | |
|
|
* | +-------------+ | |
|
|
* | | | |
|
|
* |<-get_provider_content | |
|
|
* (&ustream_instance)--+ | |
|
|
* +----------------------------malloc(content_size)-------------------------------->|
|
|
* |<--------------------------------content_ptr-------------------------------------+
|
|
* +--------------------malloc(sizeof(az_ulib_ustream_data_cb))--------------------->|
|
|
* <---------------------------------control_block_ptr-------------------------------+
|
|
* +------+ | | |
|
|
* | generate the content and store in the content_ptr | |
|
|
* +----->| | | |
|
|
* +-----az_ulib_ustream_init | |
|
|
* | (ustream_instance, | |
|
|
* | control_block_ptr, free, | |
|
|
* | content_ptr, content_size, free)------------------->| |
|
|
* | | +------+ |
|
|
* | | | data_source = content_ptr |
|
|
* | | | data_source_size = content_size
|
|
* | | +----->| |
|
|
* |<-----------------ustream_instance--------------------------+ |
|
|
* +---------AZ_OK---------->| | |
|
|
* @endcode
|
|
*
|
|
*
|
|
* Now that the consumer has it's local ustream intialized with the content, it will print it using
|
|
* the iterator az_ulib_ustream_read().
|
|
*
|
|
* @code
|
|
* | +------------------malloc(1024)------------------------>|
|
|
* | |<-----------------local_buffer-------------------------+
|
|
* .. while az_ulib_ustream_read return AZ_OK ....................................................
|
|
* : | +--az_ulib_ustream_read | | :
|
|
* : | | (ustream_instance, | | :
|
|
* : | | local_buffer, | | :
|
|
* : | | 1024, | | :
|
|
* : | | &size)------------------------>| | :
|
|
* : | | +----------------+ | :
|
|
* : | | | copy the next 1024 bytes from the | :
|
|
* : | | | data_source to the local_buffer. | :
|
|
* : | | +--------------->| | :
|
|
* : | |<-------------AZ_OK---------------+ | :
|
|
* : | +---+ | | :
|
|
* : | | use the content in the local_buffer | | :
|
|
* : | +-->| | | :
|
|
* ...............................................................................................
|
|
* | +---------------free(local_buffer)--------------------->|
|
|
* | +-az_ulib_ustream_dispose | |
|
|
* | | (ustream_instance)-------->| |
|
|
* | | +-----+ |
|
|
* | | | free(control_block_ptr)->|
|
|
* | | | free(data_source)------->|
|
|
* | | +-----+ |
|
|
* @endcode
|
|
*
|
|
* <h2>Heterogeneous buffer</h2>
|
|
* Data can be stored in multiple, different medias, like RAM, flash, file, or cloud. Each media
|
|
* has its own read requirements. A simple way to unify it is copying it all to the RAM. For
|
|
* example, if an HTTP package contains a header that is in the flash, with some data in the
|
|
* RAM and the content in a file in the external flash, to concatenate it all in a single
|
|
* datagram you can allocate a single area in the RAM that fits it all, and bring all the
|
|
* data to this memory. The problem with this approach is the amount of memory required for
|
|
* that, which can be multiple times the total RAM that you have for the entire system.
|
|
*
|
|
* A second option to solve this problem is to make each component that needs to access this data
|
|
* understand each media and implement code to handle it. This approach will not require
|
|
* storing all data in the RAM, but will increase the size of the program itself, and is not
|
|
* easily portable, as different hardware will contain different media with different
|
|
* requirements.
|
|
*
|
|
* The ustream solves this problem by creating a single interface that can handle any media,
|
|
* exposing it as a standard iterator. Whoever wants to expose a type of media as a ustream
|
|
* shall implement the functions described on the interface, handling all implementation
|
|
* details for each API. For example, the az_ulib_ustream_read() can be a simple copy of the
|
|
* flash to the RAM for a buffer that handles constants, or be as complex as creating a
|
|
* TCP/IP connection to bring the data for a buffer that handles data in the cloud.
|
|
*
|
|
* The consumer of the ustream can use all kinds of media in the same way, and may easily
|
|
* concatenate it by exposing a ustream that handles multiple ustream's.
|
|
*
|
|
* <h2>Static Memory</h2>
|
|
* The ustream does not use any calls to `malloc`. Memory that it uses is required to be passed
|
|
* by the developer with an associated release function unless otherwise specified.
|
|
*
|
|
* <h2>Ownership</h2>
|
|
* The ustream is an owner-less buffer: every instance of the ustream has the same rights. They
|
|
* all can read the ustream content, release the parts that are not necessary anymore, and
|
|
* dispose it.
|
|
* Each instance of the ustream is owned by who created it, and should never be shared by
|
|
* multiple consumers. When a consumer receives a ustream and intends to make operations over
|
|
* it, this consumer must first make a clone of the ustream, creating its own instance of it,
|
|
* and then make the needed operations.
|
|
* Cloning a ustream creates a new set of controls for the ustream that will share the same
|
|
* content of the original ustream. The content itself is a smart pointer with a `ref_count`
|
|
* that controls the total number of instances.
|
|
* Disposing an instance of the ustream will decrease the `ref_count` of this ustream. If the
|
|
* number of references reaches 0, the ustream will destroy itself by calling the provided
|
|
* release functions.
|
|
*
|
|
* @warning Not disposing an instance of the ustream will leak memory.
|
|
*
|
|
* Instances of the ustream can be created in 2 ways:
|
|
* - @b Factory - when a producer exposes data using a ustream, it must create the ustream
|
|
* using a factory, so the operation `ustream create` returns the first instance of the
|
|
* ustream.
|
|
* - @b Clone - when a consumer needs a copy of the ustream, it can use the
|
|
* az_ulib_ustream_clone().
|
|
*
|
|
* <h2>Thread Safe</h2>
|
|
* The ustream <b>IS NOT</b> thread safe for multiple accesses over the same instance. The
|
|
* ownership of the instance of a ustream shall <b>NOT</b> be shared, especially not by
|
|
* consumers that run on different threads. The owner thread shall create a clone of the
|
|
* ustream and pass it to the other thread.
|
|
* The ustream <b>IS</b> thread safe for accesses between instances. It means that any access to
|
|
* memory shared by multiple instances shall be thread safe.
|
|
*
|
|
* <h2>Data Retention</h2>
|
|
* As with any buffer, this ustream shall be used to handle data that was created by the producer
|
|
* as a result of an operation.
|
|
*
|
|
* This interface only exposes read functions, so once created, the content of the ustream cannot
|
|
* be changed by the producer or any of the consumers. Changing the content of the data
|
|
* source will result in a data mismatch.
|
|
*
|
|
* Consumers can do a partial release of the ustream by calling az_ulib_ustream_release().
|
|
* Calling the release does not imply that part of the memory will be immediately released.
|
|
* Once a ustream can handle multiple instances, a memory can only be free'd if all instances
|
|
* release it. A ustream implementation can or cannot have the ability to do partial
|
|
* releases. For instance, a ustream that handles constant data stored in the flash will
|
|
* never release any memory on the az_ulib_ustream_release() API.
|
|
*
|
|
* Released data cannot be accessed, even if it is still available in the memory.
|
|
*
|
|
* <h2>Concatenate</h2>
|
|
* New data can be concatenated at the end of the ustream by calling az_ulib_ustream_concat().
|
|
* This can include ustream's from other different medias. In this way, the ustream can
|
|
* be used as a Stream of data.
|
|
* To protect the immutability of the ustream, concatenating a new ustream to an existing one will
|
|
* only affect the instance that is calling the az_ulib_ustream_concat().
|
|
*
|
|
* <i><b>Example</b></i>
|
|
* A producer created 3 ustreams named A, B, and C. At this point, it handles one instance of each
|
|
* ustream. A consumer received an instance of the ustream A and C, and concats C to A
|
|
* creating a new ustream AC. After that, the producer will concat B to A, creating the new
|
|
* AB ustream.
|
|
*
|
|
* Observe the fact that the consumer concatenating C to A on its own instance didn't affect the
|
|
* ustream A on the producer, and when the producer concatenated B to A, it creates AB, not
|
|
* ACB, and it didn't change the consumer AB ustream creating ABC or ACB on it.
|
|
*
|
|
* <h2>Lazy</h2>
|
|
* The ustream can contain the full content, bring it into memory when required, or even create
|
|
* the content when it is necessary. The implementation of the az_ulib_ustream_read()
|
|
* function can be smart enough to use the minimal amount of memory.
|
|
*
|
|
* The only restriction is if a consumer accesses the same position of the ustream multiple
|
|
* times, it shall return the same data.
|
|
*
|
|
* <i><b>Example</b></i>
|
|
* A random number generator can expose random numbers using the ustream. To do that it shall
|
|
* generate a new number when the consumer calls az_ulib_ustream_read(). But to preserve the
|
|
* immutability, the implementation of the az_ulib_ustream_read() shall store the number in a
|
|
* recover queue, up to the point that the consumer releases this data. Because, if at some
|
|
* point in time, the consumer seeks this old position, the az_ulib_ustream_read() shall
|
|
* return the same value created in the first call of az_ulib_ustream_read().
|
|
*
|
|
* <h2>Data conversion</h2>
|
|
* When the data is copied from the data source to the local buffer, the az_ulib_ustream_read()
|
|
* may do a data conversion, which means that the content exposed on the local buffer is a
|
|
* function of the content in the data source. It directly implies that the number of bytes
|
|
* written in the local buffer may be different than the number of bytes read from the data
|
|
* source.
|
|
*
|
|
* <i><b>Example</b></i>
|
|
* A ustream can have the data source in binary format with 36 bytes, but it shall expose the
|
|
* content encoded in base64. The base64 creates 4 encoded bytes for each 3 bytes read. So,
|
|
* seeking the beginning of the file, the az_ulib_ustream_get_remaining_size() shall return
|
|
* 48 (= 36 / 3 * 4), instead of 36. If the consumer provides a local buffer of 16 bytes, the
|
|
* az_ulib_ustream_read() shall read only 12 bytes from the data source, and encode it in
|
|
* base64 expanding the 12 bytes to 16 bytes on the local buffer.
|
|
* @code
|
|
* ustream domain :: consumer domain
|
|
* ::
|
|
* Data source ::
|
|
* +-------+--------------------+ ::
|
|
* binary data --> | | | ::
|
|
* +-------+--------------------+ ::
|
|
* inner position --> 0 12 36 ::
|
|
* \--+--/ :: Local buffer
|
|
* | size = 12 :: +----------------+
|
|
* +---> base64 encoder ---------------> | base64 |
|
|
* :: +----------------+
|
|
* :: size' = 16
|
|
* @endcode
|
|
*
|
|
* <h2>Data offset</h2>
|
|
* In the data source, each byte is associated with a position, called `inner position`. The
|
|
* first byte is always placed at the inner position `0`, followed by the other bytes which
|
|
* are incremented in a sequential manner. The ustream assigns a sequential number to each
|
|
* byte in the local buffer as well, called `logical position`. When a new ustream is
|
|
* created, the logical position matches the inner position, both starting at position `0`.
|
|
*
|
|
* When the ustream is cloned, an offset shall be provided. This offset is the new first logical
|
|
* position. The implementation of the ustream shall handle the difference between the inner
|
|
* and logical position, making the conversion in all the ustream API. Providing an offset to
|
|
* a ustream can be useful in many cases. For example, to concatenate buffers, the second
|
|
* ustream can have an offset of the end of the first ustream plus one, or in a TCP
|
|
* connection, make the logical position the same value of the octet sequence number.
|
|
*
|
|
* <i><b>Example</b></i>
|
|
* A ustream was created from the flash with 100 bytes. The inner position is a sequence from
|
|
* `0` to `99`, and it matches the logical position. The consumer clones this ustream
|
|
* providing an offset of `1000`. The new instance contains the same content as the original
|
|
* one, but the logical positions are now from `1000` to `1099`.
|
|
*
|
|
* If the owner of the first instance wants to set the position to position 10, it shall call
|
|
* az_ulib_ustream_set_position() with the logical position 10. For the cloned instance, to
|
|
* set the position to the same position 10, it shall call az_ulib_ustream_set_position()
|
|
* with the logical position 1010.
|
|
*
|
|
* <h2>Sliding window</h2>
|
|
* One of the target use cases of the ustream is to accelerate and simplify the implementation of
|
|
* sliding window protocols, like TCP. As described in this document, the ustream associates
|
|
* a single byte (octet) to a single position, which means that every byte can be accessed by
|
|
* its position. For the consumer, this position is the logical position.
|
|
*
|
|
* To better understand the sliding window concept of the ustream, the Data source can be split
|
|
* in 4 segments.
|
|
*
|
|
* @code
|
|
* Data Source:
|
|
* Released Pending Future
|
|
* |----------------|---------------------:--------------------|---------------------|
|
|
* |\ \ : Read |\ |
|
|
* | 0 First Valid Position : | Current Position |
|
|
* | : | |
|
|
* | :<--- Read Size ---->| |
|
|
* | |
|
|
* |<------------------------------ Data Source Size ------------------------------->|
|
|
* @endcode
|
|
* - @b Released - Sequence of bytes in the data source that is already acknowledged by the
|
|
* consumer, and shall not be accessed anymore.
|
|
* - @b Pending - Sequence of bytes in the data source that is already read by the consumer,
|
|
* but not acknowledged yet. The consumer can seek these bytes with
|
|
* az_ulib_ustream_set_position() and read it again. This sequence starts at the `First
|
|
* Valid Position` and ends at the last byte before the `Current Position`.
|
|
* - @b Read - The last read portion of the data source. On the read operation, the `Read`
|
|
* starts at the `Current Position` up to the `Read Size`. At the end of the read, this
|
|
* segment is incorporated to `Pending` by changing the `Current Position` to the end of
|
|
* the Read.
|
|
* - @b Future - Sequence of bytes in the data source that is not read by the consumer yet.
|
|
* It starts at the `Current Position` and ends at the end of the data source, which has
|
|
* the position calculated by `Data Source Size - 1`.
|
|
*
|
|
* To read a new portion of the data source, the consumer shall provide memory (the local
|
|
* buffer), where the implementation of the ustream will write the bytes that were read and
|
|
* converted from the data source. The consumer can use this data in its own context: for
|
|
* example, to transmit as a TCP packet. When the consumer finishes using the data in the
|
|
* local buffer, this data can be discarded and the local buffer recycled to get the next
|
|
* portion of the data source.
|
|
*
|
|
* If at some point in the future, the consumer needs this data again, it can set the position to
|
|
* the needed position and get the same content using the read.
|
|
*
|
|
* The consumer may confirm that a portion of the data is not necessary anymore. For example,
|
|
* after transmitting multiple TCP packets, the receiver of these packets answers with an ACK
|
|
* for a sequence number. In this case, the consumer can release this data in the data source
|
|
* by calling the az_ulib_ustream_release(), moving the `First Valid Position` to the next
|
|
* one after the released position.
|
|
*
|
|
* A common scenario is when the consumer needs to read over the data source starting on the
|
|
* first byte after the last released one. For example, when a timeout happens for a
|
|
* transmitted packet without ACK, the sender shall retransmit the data starting from that
|
|
* point. In this case, the consumer can call the API az_ulib_ustream_reset().
|
|
*/
|
|
|
|
#include "az_ulib_base.h"
|
|
#include "az_ulib_config.h"
|
|
#include "az_ulib_pal_api.h"
|
|
#include "az_ulib_result.h"
|
|
|
|
#ifdef __cplusplus
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#else
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#endif /* __cplusplus */
|
|
|
|
#include "azure/core/_az_cfg_prefix.h"
|
|
|
|
/**
|
|
* @brief Forward declaration of az_ulib_ustream. See #az_ulib_ustream_tag for struct members.
|
|
*/
|
|
typedef struct az_ulib_ustream_tag az_ulib_ustream;
|
|
|
|
/**
|
|
* @brief vTable with the ustream APIs.
|
|
*
|
|
* Any module that exposes the ustream shall implement the functions on this vTable.
|
|
*
|
|
* Any code that will use an exposed ustream shall call the APIs using the `az_ulib_ustream_...`
|
|
* inline functions.
|
|
*/
|
|
typedef struct az_ulib_ustream_interface_tag
|
|
{
|
|
/** Concrete `set_position` implementation. */
|
|
az_result (*set_position)(az_ulib_ustream* ustream_instance, offset_t position);
|
|
|
|
/** Concrete `reset` implementation. */
|
|
az_result (*reset)(az_ulib_ustream* ustream_instance);
|
|
|
|
/** Concrete `read` implementation. */
|
|
az_result (*read)(
|
|
az_ulib_ustream* ustream_instance,
|
|
uint8_t* const buffer,
|
|
size_t buffer_length,
|
|
size_t* const size);
|
|
|
|
/** Concrete `get_remaining_size` implementation. */
|
|
az_result (*get_remaining_size)(az_ulib_ustream* ustream_instance, size_t* const size);
|
|
|
|
/** Concrete `get_position` implementation. */
|
|
az_result (*get_position)(az_ulib_ustream* ustream_instance, offset_t* const position);
|
|
|
|
/** Concrete `release` implementation. */
|
|
az_result (*release)(az_ulib_ustream* ustream_instance, offset_t position);
|
|
|
|
/** Concrete `clone` implementation. */
|
|
az_result (*clone)(
|
|
az_ulib_ustream* ustream_instance_clone,
|
|
az_ulib_ustream* ustream_instance,
|
|
offset_t offset);
|
|
|
|
/** Concrete `dispose` implementation. */
|
|
az_result (*dispose)(az_ulib_ustream* ustream_instance);
|
|
|
|
} az_ulib_ustream_interface;
|
|
|
|
/**
|
|
* @brief Pointer to the data from which to read
|
|
*
|
|
* void pointer to memory where the data is located or any needed controls to access the data.
|
|
* The content of the memory to which this points is up to the ustream implementation.
|
|
*
|
|
*/
|
|
typedef void* az_ulib_ustream_data;
|
|
|
|
/**
|
|
* @brief Structure for data control block
|
|
*
|
|
* For any given ustream that is created, one control block is created and initialized.
|
|
*
|
|
* @note This structure should be viewed and used as internal to the implementation of the
|
|
* ustream. Users should therefore not act on it directly and only allocate the memory
|
|
* necessary for it to be passed to the ustream.
|
|
*
|
|
*/
|
|
typedef struct az_ulib_ustream_data_cb_tag
|
|
{
|
|
/** The #az_ulib_ustream_interface* for this ustream instance type. */
|
|
const az_ulib_ustream_interface* api;
|
|
|
|
/** The #az_ulib_ustream_data* pointing to the data to read. It can be anything that a given
|
|
* ustream implementation needs to access the data, whether it be a memory address to a buffer,
|
|
* another struct with more controls, etc */
|
|
const az_ulib_ustream_data* ptr;
|
|
|
|
/** The `volatile long` with the number of references taken for this memory. */
|
|
volatile long ref_count;
|
|
|
|
/** The #az_ulib_release_callback to call to release `ptr` once the `ref_count` goes to zero. */
|
|
az_ulib_release_callback data_release;
|
|
|
|
/** The #az_ulib_release_callback to call to release the #az_ulib_ustream_data_cb once the
|
|
* `ref_count` goes to zero */
|
|
az_ulib_release_callback control_block_release;
|
|
|
|
} az_ulib_ustream_data_cb;
|
|
|
|
/**
|
|
* @brief Structure for instance control block
|
|
*
|
|
* For any given ustream that is created, there may be mutliple `az_ulib_ustream`'s
|
|
* pointing to the same `az_ulib_ustream_data_cb`. Each instance control block serves to
|
|
* manage a given developer's usage of the memory pointed to inside the
|
|
* `az_ulib_ustream_data_cb`. Each time an `az_ulib_ustream` is cloned using
|
|
* az_ulib_ustream_clone(), the `ref_count` inside the `az_ulib_ustream_data_cb` is
|
|
* incremented to signal a reference to the memory has been acquired. Once the instance is
|
|
* done being used, az_ulib_ustream_release() must be called to decrement `ref_count`.
|
|
*
|
|
* @note This structure should be viewed and used as internal to the implementation of the
|
|
* ustream. Users should therefore not act on it directly and only allocate the memory
|
|
* necessary for it to be passed to the ustream.
|
|
*/
|
|
struct az_ulib_ustream_tag
|
|
{
|
|
/**
|
|
* @brief Control Block.
|
|
*
|
|
* The #az_ulib_ustream_data_cb* on which this instance operates on.
|
|
*/
|
|
az_ulib_ustream_data_cb* control_block;
|
|
|
|
/* Instance controls. */
|
|
|
|
/** The #offset_t used as the logical position for this instance. */
|
|
offset_t offset_diff;
|
|
|
|
/** The #offset_t used to keep track of the current position (next returned position). */
|
|
offset_t inner_current_position;
|
|
|
|
/** The #offset_t used to keep track of the earliest position to reset. */
|
|
offset_t inner_first_valid_position;
|
|
|
|
/** The `size_t` with the length of the data in the control_block. */
|
|
size_t length;
|
|
};
|
|
|
|
/**
|
|
* @brief Structure to keep track of concatenated ustreams.
|
|
*
|
|
* When concatenating a ustream to another ustream, the instances are placed into a
|
|
* `az_ulib_ustream_multi_data_cb`. The base ustream onto which you wish to concatenate will
|
|
* be copied into the `ustream_one` structure and the ustream to concatenate will be cloned
|
|
* into the `ustream_two` structure. The difference being that the first #az_ulib_ustream*,
|
|
* when returned, will point to the newly populated multi instance and the ownership of the
|
|
* passed instance will be assumed by the multi instance. The second ustream which was passed
|
|
* will not be changed, only cloned into the `az_ulib_ustream_multi_data_cb` structure.
|
|
*
|
|
* @note This structure should be viewed and used as internal to the implementation of the
|
|
* ustream. Users should therefore not act on it directly and only allocate the memory
|
|
* necessary for it to be passed to the ustream.
|
|
*
|
|
*/
|
|
typedef struct az_ulib_ustream_multi_data_cb_tag
|
|
{
|
|
/** The #az_ulib_ustream_data_cb to manage the multi data structure. */
|
|
az_ulib_ustream_data_cb control_block;
|
|
|
|
/** The #az_ulib_ustream with the first ustream instance. */
|
|
az_ulib_ustream ustream_one;
|
|
|
|
/** The #az_ulib_ustream with the second ustream instance. */
|
|
az_ulib_ustream ustream_two;
|
|
|
|
/** The `long` with the number of references to the first ustream. */
|
|
volatile long ustream_one_ref_count;
|
|
|
|
/** The `long` with the number of references to the second ustream. */
|
|
volatile long ustream_two_ref_count;
|
|
|
|
/** The #az_ulib_pal_os_lock with controls the critical section of the read from the multi
|
|
* ustream. */
|
|
az_ulib_pal_os_lock lock;
|
|
} az_ulib_ustream_multi_data_cb;
|
|
|
|
/**
|
|
* @brief Check if a handle is the same type of the API.
|
|
*
|
|
* It will return true if the handle is valid and it is the same type of the API. It will
|
|
* return false if the handle is `NULL` or not the correct type.
|
|
*/
|
|
#define AZ_ULIB_USTREAM_IS_TYPE_OF(handle, type_api) \
|
|
(!((handle == NULL) || (handle->control_block == NULL) || (handle->control_block->api == NULL) \
|
|
|| (handle->control_block->api != &type_api)))
|
|
|
|
/**
|
|
* @brief Change the current position of the ustream.
|
|
*
|
|
* The current position is the one that will be returned in the local buffer by the next
|
|
* az_ulib_ustream_read(). Consumers can call this API to go back or forward, but it cannot
|
|
* exceed the end of the ustream or precede the fist valid position (last released
|
|
* position + 1).
|
|
*
|
|
* The `az_ulib_ustream_set_position` API shall follow these minimum requirements:
|
|
* - The `set_position` shall change the current position of the ustream.
|
|
* - If the provided position is out of the range of the ustream, the `set_position` shall
|
|
* return #AZ_ERROR_ITEM_NOT_FOUND, and will not change the current position.
|
|
* - If the provided position is already released, the `set_position` shall return
|
|
* #AZ_ERROR_ITEM_NOT_FOUND, and will not change the current position.
|
|
* - If the provided interface is `NULL`, the `set_position` shall fail with precondition.
|
|
* - If the provided interface is not the implemented ustream type, the `set_position` fail
|
|
* with precondition.
|
|
*
|
|
* @param[in] ustream_instance The #az_ulib_ustream* with the interface of the ustream.
|
|
* @param[in] position The `offset_t` with the new current position in the ustream.
|
|
*
|
|
* @pre \p ustream_instance shall not be `NULL`.
|
|
* @pre \p ustream_instance shall be a valid ustream that is the implemented ustream type.
|
|
*
|
|
* @return The #az_result with the result of the `set_position` operation.
|
|
* @retval #AZ_OK If the ustream changed the current position with
|
|
* success.
|
|
* @retval #AZ_ERROR_ULIB_BUSY If the resource necessary for the `set_position`
|
|
* operation is busy.
|
|
* @retval #AZ_ERROR_CANCELED If the `set_position` operation was cancelled.
|
|
* @retval #AZ_ERROR_ITEM_NOT_FOUND If the position is out of the ustream range.
|
|
* @retval #AZ_ERROR_NOT_ENOUGH_SPACE If there is not enough memory to execute the
|
|
* `set_position` operation.
|
|
* @retval #AZ_ERROR_ULIB_SECURITY If the `set_position` operation was denied for
|
|
* security reasons.
|
|
* @retval #AZ_ERROR_ULIB_SYSTEM If the `set_position` operation failed on the
|
|
* system level.
|
|
*/
|
|
AZ_INLINE az_result
|
|
az_ulib_ustream_set_position(az_ulib_ustream* ustream_instance, offset_t position)
|
|
{
|
|
return ustream_instance->control_block->api->set_position(ustream_instance, position);
|
|
}
|
|
|
|
/**
|
|
* @brief Changes the current position to the first valid position.
|
|
*
|
|
* The current position is the one that will be returned in the local buffer by the next
|
|
* az_ulib_ustream_read(). Reset will bring the current position to the first valid one,
|
|
* which is the first byte after the released position.
|
|
*
|
|
* The `az_ulib_ustream_reset` API shall follow the following minimum requirements:
|
|
* - The `reset` shall change the current position of the ustream to the first byte after the
|
|
* released position.
|
|
* - If all bytes are already released, the ustream `reset` shall return
|
|
* #AZ_ERROR_ITEM_NOT_FOUND, and will not change the current position.
|
|
* - If the provided interface is `NULL`, the ustream `reset` shall fail with pre-condition.
|
|
* - If the provided interface is not the implemented ustream type, the ustream `reset` shall
|
|
* fail with pre-condition.
|
|
*
|
|
* @param[in] ustream_instance The #az_ulib_ustream* with the interface of the ustream.
|
|
*
|
|
* @pre \p ustream_instance shall not be `NULL`.
|
|
* @pre \p ustream_instance shall be a valid ustream that is the implemented ustream type.
|
|
*
|
|
* @return The #az_result with the result of the `reset` operation.
|
|
* @retval #AZ_OK If the ustream changed the current position with
|
|
* success.
|
|
* @retval #AZ_ERROR_ULIB_BUSY If the resource necessary for the `reset`
|
|
* operation is busy.
|
|
* @retval #AZ_ERROR_CANCELED If the `reset` operation was cancelled.
|
|
* @retval #AZ_ERROR_ITEM_NOT_FOUND If all previous bytes in the ustream were already
|
|
* released.
|
|
* @retval #AZ_ERROR_NOT_ENOUGH_SPACE If there is not enough memory to execute the
|
|
* `reset` operation.
|
|
* @retval #AZ_ERROR_ULIB_SECURITY If the `reset` operation was denied for security
|
|
* reasons.
|
|
* @retval #AZ_ERROR_ULIB_SYSTEM If the `reset` operation failed on the system level.
|
|
*/
|
|
AZ_INLINE az_result az_ulib_ustream_reset(az_ulib_ustream* ustream_instance)
|
|
{
|
|
return ustream_instance->control_block->api->reset(ustream_instance);
|
|
}
|
|
|
|
/**
|
|
* @brief Gets the next portion of the ustream starting at the current position.
|
|
*
|
|
* The `az_ulib_ustream_read` API will copy the contents of the Data source to the local buffer
|
|
* starting at the current position. The local buffer is the one referenced by the parameter
|
|
* `buffer`, and with the maximum size `buffer_length`.
|
|
*
|
|
* The buffer is defined as a `uint8_t*` and can represent any sequence of data. Pay
|
|
* special attention, if the data is a string, the buffer will still copy it as a sequence of
|
|
* `uint8_t`, and will <b>NOT</b> put any terminator at the end of the string. The size of
|
|
* the content copied in the local buffer will be returned in the parameter `size`.
|
|
*
|
|
* The `az_ulib_ustream_read` API shall follow the following minimum requirements:
|
|
* - The read shall copy the contents of the `Data Source` to the provided local buffer.
|
|
* - If the contents of the `Data Source` is bigger than the `buffer_length`, the read shall
|
|
* limit the copy size up to the buffer_length.
|
|
* - The read shall return the number of valid `uint8_t` values in the local buffer in
|
|
* the provided `size`.
|
|
* - If there is no more content to return, the read shall return
|
|
* #AZ_ULIB_EOF, size shall be set to 0, and will not change the contents
|
|
* of the local buffer.
|
|
* - If the provided buffer_length is zero, the read shall fail with precondition.
|
|
* - If the provided buffer_length is lower than the minimum number of bytes that the ustream
|
|
* can copy, the read shall fail with pre-condition.
|
|
* - If the provided interface is `NULL`, the read shall fail with precondition.
|
|
* - If the provided interface is not the implemented ustream type, the read shall fail with
|
|
* pre-condition.
|
|
* - If the provided local buffer is `NULL`, the read shall fail with precondition.
|
|
* - If the provided return size pointer is `NULL`, the read shall fail with precondition.
|
|
*
|
|
* @param[in] ustream_instance The #az_ulib_ustream* with the interface of the ustream.
|
|
* @param[out] buffer The `uint8_t* const` that points to the local buffer.
|
|
* @param[in] buffer_length The `size_t` with the size of the local buffer.
|
|
* @param[out] size The `size_t* const` that points to the place where the
|
|
* read shall store the number of valid `uint8_t` values
|
|
* returned in the local buffer.
|
|
*
|
|
* @pre \p ustream_instance shall not be `NULL`.
|
|
* @pre \p ustream_instance shall be a valid ustream that is the implemented ustream type.
|
|
* @pre \p buffer shall not be `NULL`.
|
|
* @pre \p buffer_length shall not be bigger than 0.
|
|
* @pre \p size shall not be `NULL`.
|
|
*
|
|
* @return The #az_result with the result of the read operation.
|
|
* @retval #AZ_OK If the ustream copied the content of the `Data
|
|
* Source` to the local buffer with success.
|
|
* @retval #AZ_ERROR_ULIB_BUSY If the resource necessary to read the ustream
|
|
* content is busy.
|
|
* @retval #AZ_ERROR_CANCELED If the read of the content was cancelled.
|
|
* @retval #AZ_ULIB_EOF If there are no more `uint8_t` values in the `Data
|
|
* Source` to read.
|
|
* @retval #AZ_ERROR_NOT_ENOUGH_SPACE If there is not enough memory to execute the read.
|
|
* @retval #AZ_ERROR_ULIB_SECURITY If the read was denied for security reasons.
|
|
* @retval #AZ_ERROR_ULIB_SYSTEM If the read operation failed on the system level.
|
|
*/
|
|
AZ_INLINE az_result az_ulib_ustream_read(
|
|
az_ulib_ustream* ustream_instance,
|
|
uint8_t* const buffer,
|
|
size_t buffer_length,
|
|
size_t* const size)
|
|
{
|
|
return ustream_instance->control_block->api->read(ustream_instance, buffer, buffer_length, size);
|
|
}
|
|
|
|
/**
|
|
* @brief Returns the remaining size of the ustream.
|
|
*
|
|
* This API returns the number of bytes between the current position and the end of the ustream.
|
|
*
|
|
* The `az_ulib_ustream_get_remaining_size` API shall follow the following minimum requirements:
|
|
* - The `get_remaining_size` shall return the number of bytes between the current position
|
|
* and the end of the ustream.
|
|
* - If the provided interface is `NULL`, the `get_remaining_size` shall fail with
|
|
* precondition.
|
|
* - If the provided interface is not the implemented ustream type, the `get_remaining_size`
|
|
* shall fail with precondition.
|
|
* - If the provided size is `NULL`, the `get_remaining_size` shall fail with precondition.
|
|
*
|
|
* @param[in] ustream_instance The #az_ulib_ustream* with the interface of the ustream.
|
|
* @param[out] size The `size_t* const` to return the remaining number of
|
|
* `uint8_t` values.
|
|
*
|
|
* @pre \p ustream_instance shall not be `NULL`.
|
|
* @pre \p ustream_instance shall be a valid ustream that is the implemented ustream type.
|
|
* @pre \p size shall not be `NULL`.
|
|
*
|
|
* @return The #az_result with the result of the `get_remaining_size` operation.
|
|
* @retval #AZ_OK If it succeeded to get the remaining size of the
|
|
* ustream.
|
|
* @retval #AZ_ERROR_ULIB_BUSY If the resource necessary to get the remaining
|
|
* size of the ustream is busy.
|
|
* @retval #AZ_ERROR_CANCELED If the `get_remaining_size` was cancelled.
|
|
* @retval #AZ_ERROR_NOT_ENOUGH_SPACE If there is not enough memory to execute the
|
|
* `get_remaining_size` operation.
|
|
* @retval #AZ_ERROR_ULIB_SECURITY If the `get_remaining_size` was denied for
|
|
* security reasons.
|
|
* @retval #AZ_ERROR_ULIB_SYSTEM If the `get_remaining_size` operation failed on
|
|
* the system level.
|
|
*/
|
|
AZ_INLINE az_result
|
|
az_ulib_ustream_get_remaining_size(az_ulib_ustream* ustream_instance, size_t* const size)
|
|
{
|
|
return ustream_instance->control_block->api->get_remaining_size(ustream_instance, size);
|
|
}
|
|
|
|
/**
|
|
* @brief Returns the current position in the ustream.
|
|
*
|
|
* This API returns the logical current position.
|
|
*
|
|
* The `az_ulib_ustream_get_position` API shall follow the following minimum requirements:
|
|
* - The `get_position` shall return the logical current position of the ustream.
|
|
* - If the provided interface is `NULL`, the `get_position` shall fail with precondition.
|
|
* - If the provided interface is not the implemented ustream type, the `get_position`
|
|
* shall fail with precondition.
|
|
* - If the provided position is `NULL`, the `get_position` shall fail with precondition.
|
|
*
|
|
* @param[in] ustream_instance The #az_ulib_ustream* with the interface of the ustream.
|
|
* @param[out] position The `offset_t* const` to return the logical current position
|
|
* in the ustream.
|
|
*
|
|
* @pre \p ustream_instance shall not be `NULL`.
|
|
* @pre \p ustream_instance shall be a valid ustream that is the implemented ustream type.
|
|
* @pre \p position shall not be `NULL`.
|
|
*
|
|
* @return The #az_result with the result of the `get_position` operation.
|
|
* @retval #AZ_OK If it provided the position of the ustream.
|
|
* @retval #AZ_ERROR_ULIB_BUSY If the resource necessary for getting the
|
|
* position is busy.
|
|
* @retval #AZ_ERROR_CANCELED If the `get_position` was cancelled.
|
|
* @retval #AZ_ERROR_NOT_ENOUGH_SPACE If there is not enough memory to execute the
|
|
* `get_position` operation.
|
|
* @retval #AZ_ERROR_ULIB_SECURITY If the `get_position` was denied for
|
|
* security reasons.
|
|
* @retval #AZ_ERROR_ULIB_SYSTEM If the `get_position` operation failed on
|
|
* the system level.
|
|
*/
|
|
AZ_INLINE az_result
|
|
az_ulib_ustream_get_position(az_ulib_ustream* ustream_instance, offset_t* const position)
|
|
{
|
|
return ustream_instance->control_block->api->get_position(ustream_instance, position);
|
|
}
|
|
|
|
/**
|
|
* @brief Releases all the resources related to the `Data Source` before and including the
|
|
* released position.
|
|
*
|
|
* Calling this API will notify the ustream that the developer will not need its content from the
|
|
* start to `position` (inclusive). It means that the implementation of the ustream can
|
|
* dispose any resources allocated to control and maintain this part of the ustream.
|
|
* It is up to the implementation of the ustream to decide to release any resource. For example,
|
|
* if the ustream is a string in the Flash, it does not make sense to release it.
|
|
* The provided position shall be the logical position, and it shall be between the logical first
|
|
* valid position of the ustream and the logical current position minus one. For example, the
|
|
* following code releases all bytes from the start to the last received position:
|
|
*
|
|
* @code
|
|
* offset_t pos;
|
|
* if(az_ulib_ustream_get_position(my_buffer, &pos) == AZ_OK)
|
|
* {
|
|
* az_ulib_ustream_release(my_buffer, pos - 1);
|
|
* }
|
|
* @endcode
|
|
*
|
|
* The `az_ulib_ustream_release` API shall follow the following minimum requirements:
|
|
* - The `release` shall dispose all resources necessary to handle the content of ustream
|
|
* before and including the release position.
|
|
* - If the release position is after the current position or the ustream size, the `release`
|
|
* shall return #AZ_ERROR_ARG.
|
|
* - If the release position is already released, the `release` shall return
|
|
* #AZ_ERROR_ITEM_NOT_FOUND, and do not release any resource.
|
|
* - If the provided interface is `NULL`, the `release` shall fail with precondition.
|
|
* - If the provided interface is not the implemented ustream type, the `release` shall
|
|
* fail with precondition.
|
|
*
|
|
* @param[in] ustream_instance The #az_ulib_ustream* with the interface of the ustream.
|
|
* @param[in] position The `offset_t` with the position in the ustream to release.
|
|
* The ustream will release the `uint8_t` on the position and all
|
|
* `uint8_t` before the position. It shall be bigger than 0.
|
|
*
|
|
* @pre \p ustream_instance shall not be `NULL`.
|
|
* @pre \p ustream_instance shall be a valid ustream that is the implemented ustream type.
|
|
*
|
|
* @return The #az_result with the result of the `release` operation.
|
|
* @retval #AZ_OK If the ustream releases the position with success.
|
|
* @retval #AZ_ERROR_ITEM_NOT_FOUND If the position is already released.
|
|
* @retval #AZ_ERROR_ULIB_SYSTEM If the `release` operation failed on the system
|
|
* level.
|
|
* @retval #AZ_ERROR_ARG If the position is out of range.
|
|
*/
|
|
AZ_INLINE az_result az_ulib_ustream_release(az_ulib_ustream* ustream_instance, offset_t position)
|
|
{
|
|
return ustream_instance->control_block->api->release(ustream_instance, position);
|
|
}
|
|
|
|
/**
|
|
* @brief Initializes a new instance of the ustream and returns it.
|
|
*
|
|
* Cloning a ustream will create a new instance of the ustream that shares the same content of
|
|
* the original one. The clone shall not copy the content of the ustream, but only add a
|
|
* reference to it.
|
|
*
|
|
* Both the start position and the current position of the cloned ustream will be the current
|
|
* position of the original ustream. The logical position of it will be determined by the
|
|
* provided offset.
|
|
*
|
|
* The size of the new ustream will be the remaining size of the original ustream, which is the
|
|
* size minus the current position.
|
|
*
|
|
* <i><b>Example 1</b></i>
|
|
*
|
|
* Consider a ustream with 1500 bytes, that was created from the factory, with `Logical` and
|
|
* `Inner` positions equal to `0`. After some operations, 1000 bytes were read (from 0 to
|
|
* 999). The current position is `1000`, and 200 bytes were released (from 0 to 199), so the
|
|
* released position is `199`.
|
|
* For the following examples, the positions are represented by `[Logical, Inner]`.
|
|
*
|
|
* Original ustream:
|
|
*
|
|
* @code
|
|
* | Released | Pending | Future |
|
|
* |-----------------|--------------------------|-----------------------------|
|
|
* |<- start [0, 0] |<- released [199, 199] |<- current [1000, 1000] |<- end [1499, 1499]
|
|
* @endcode
|
|
*
|
|
* Cloning the original ustream with offset 0 will result in the following ustream:
|
|
*
|
|
* @code
|
|
* ||| Future |
|
|
* |||-----------------------------------|
|
|
* released [-1, 999] ->|||<- start, current [0, 1000] |<- end [499, 1499]
|
|
* @endcode
|
|
*
|
|
* Cloning the same original ustream with offset 100 will result in the following ustream:
|
|
*
|
|
* @code
|
|
* ||| Future |
|
|
* |||-----------------------------------|
|
|
* released [99, 999] ->|||<- start, current [100, 1000] |<- end [599, 1499]
|
|
* @endcode
|
|
*
|
|
* <i><b>Example 2</b></i>
|
|
*
|
|
* Consider a ustream with 5000 bytes, that was created from the factory, with `Logical` and
|
|
* `Inner` positions equal to `0`. After some operations, 250 bytes were read (from 0 to
|
|
* 249), so the current position is `250`, and no release was made, so the released position
|
|
* is still `-1`.
|
|
*
|
|
* For the following examples, the positions are represented by `[Logical, Inner]`.
|
|
*
|
|
*
|
|
* Original ustream:
|
|
*
|
|
* @code
|
|
* || Pending | Future |
|
|
* ||-----------------+-----------------------------------|
|
|
* released [-1, 0] ->||<- start [0, 0] |<- current [250, 250] |<- end [4999, 4999]
|
|
* @endcode
|
|
*
|
|
* Cloning this original ustream with offset 10000 will result in the following ustream:
|
|
*
|
|
* @code
|
|
* ||| Future |
|
|
* |||--------------------------------------|
|
|
* released [9999, 249] ->|||<- start, current [10000, 250] |<- end [14749, 4999]
|
|
* @endcode
|
|
*
|
|
* <i><b>Example 3</b></i>
|
|
*
|
|
* From the previous cloned ustream, after some operations, the `Logical` current position is
|
|
* moved to `11000`, and the `Logical` released position is `10499`.
|
|
|
|
* For the following examples, the positions are represented by `[Logical, Inner]`.
|
|
*
|
|
* Previous cloned ustream:
|
|
*
|
|
* @code
|
|
* | Released | Pending | Future |
|
|
* |----------------------+-------------------------+-------------------------|
|
|
* |<- start [10000, 250] |<- released [10499, 749] |<- current [11000, 1250] |<- end[14749, 4999]
|
|
* @endcode
|
|
*
|
|
* Cloning this cloned ustream with offset 0 will result in the following ustream:
|
|
*
|
|
* @code
|
|
* ||| Future |
|
|
* |||--------------------------------------|
|
|
* released [-1, 1249] ->|||<- start, current [0, 1250] |<- end [3749, 4999]
|
|
* @endcode
|
|
*
|
|
* @note From the point of view of a consumer, the `Inner` position never matters, it will
|
|
* always use the `Logical` position for all operations.
|
|
* @note If the position is not important to the consumer, making the offset equal to `0` is a
|
|
* safe option.
|
|
*
|
|
* The `az_ulib_ustream_clone` API shall follow the following minimum requirements:
|
|
* - The `clone` shall return a ustream with the same content of the original ustream.
|
|
* - If the provided interface is `NULL`, the `clone` shall return `NULL`.
|
|
* - If the provided interface is not a type of the implemented ustream, the `clone` shall
|
|
* return `NULL`.
|
|
* - If there is not enough memory to control the new ustream, the `clone` shall return
|
|
* `NULL`.
|
|
* - If the provided offset plus the ustream size is bigger than the maximum size_t, the
|
|
* `clone` shall return `NULL`.
|
|
* - The cloned ustream shall not interfere with the instance of the original ustream and
|
|
* vice versa.
|
|
*
|
|
* @param[out] ustream_instance_clone The #az_ulib_ustream* with the interface of the ustream.
|
|
* @param[in] ustream_instance The #az_ulib_ustream* to be cloned.
|
|
* @param[out] offset The `offset_t` with the `Logical` position of the first
|
|
* byte in the cloned ustream.
|
|
*
|
|
* @pre \p ustream_instance shall not be `NULL`.
|
|
* @pre \p ustream_instance shall be a valid ustream that is the implemented ustream type.
|
|
* @pre \p ustream_instance_clone shall not be `NULL`.
|
|
*
|
|
* @return The #az_result with the result of the clone operation.
|
|
* @retval #AZ_OK If the ustream was cloned with success.
|
|
* @retval #AZ_ERROR_ARG If the offset make buffer size bigger than UINT32_MAX.
|
|
*/
|
|
AZ_INLINE az_result az_ulib_ustream_clone(
|
|
az_ulib_ustream* ustream_instance_clone,
|
|
az_ulib_ustream* ustream_instance,
|
|
offset_t offset)
|
|
{
|
|
return ustream_instance->control_block->api->clone(
|
|
ustream_instance_clone, ustream_instance, offset);
|
|
}
|
|
|
|
/**
|
|
* @brief Release all the resources allocated to control the instance of the ustream.
|
|
*
|
|
* The dispose will release the instance of the ustream and decrement the reference of the
|
|
* ustream. If there are no more references to the ustream, the dispose will release all
|
|
* resources allocated to control the ustream.
|
|
*
|
|
* The `az_ulib_ustream_dispose` API shall follow the following minimum requirements:
|
|
* - The `dispose` shall free all allocated resources for the instance of the ustream.
|
|
* - If there are no more instances of the ustream, the `dispose` shall release all allocated
|
|
* resources to control the ustream.
|
|
* - If the provided interface is `NULL`, the `dispose` shall fail with precondition.
|
|
* - If the provided interface is not the type of the implemented ustream, the `dispose`
|
|
* shall fail with precondition.
|
|
*
|
|
* @param[in] ustream_instance The #az_ulib_ustream* with the interface of the ustream.
|
|
*
|
|
* @pre \p ustream_instance shall not be `NULL`.
|
|
* @pre \p ustream_instance shall be a valid ustream that is the implemented ustream type.
|
|
*
|
|
* @return The #az_result with the result of the `dispose` operation.
|
|
* @retval #AZ_OK If the instance of the ustream was disposed
|
|
* with success.
|
|
*/
|
|
AZ_INLINE az_result az_ulib_ustream_dispose(az_ulib_ustream* ustream_instance)
|
|
{
|
|
return ustream_instance->control_block->api->dispose(ustream_instance);
|
|
}
|
|
|
|
#include "azure/core/_az_cfg_suffix.h"
|
|
|
|
#endif /* AZ_ULIB_USTREAM_BASE_H */
|