// This is the implementation of a byte queue useful // for systems that need to process engs of bytes. // // It features sticky errors, a zero-copy interface, // and a safe mechanism to patch previously written // bytes. // // Only up to 4GB of data can be stored at once. // Internal use only enum { BYTE_QUEUE_ERROR = 1 << 0, BYTE_QUEUE_READ = 1 << 1, BYTE_QUEUE_WRITE = 1 << 2, }; typedef struct { uint8_t *ptr; size_t len; } ByteView; // Fields are for internal use only typedef struct { uint64_t curs; uint8_t* data; uint32_t head; uint32_t size; uint32_t used; uint32_t limit; uint8_t* read_target; uint32_t read_target_size; int flags; } ByteQueue; // Represents an offset inside the queue relative // to the first byte ever appended to the queue, // therefore consuming bytes from the queue does // not invalidate this type of offset. typedef uint64_t ByteQueueOffset; // Initialize the queue with a given capacity limit. // This is just a soft limit. The queue will allocate // dynamically as needed up to this limit and won't // grow further. When the limit is reached, http_queue_full // returns true. void byte_queue_init(ByteQueue *queue, uint32_t limit); // Free resources associated to this queue void byte_queue_free(ByteQueue *queue); // Check whether an error occurred inside the queue int byte_queue_error(ByteQueue *queue); // Returns 1 if the queue has no bytes inside it, // or 0 otherwise. int byte_queue_empty(ByteQueue *queue); // Returns 1 if the queue reached its limit, or 0 // otherwise. int byte_queue_full(ByteQueue *queue); // These two functions are to be used together. // read_buf returns a view into the queue of the // bytes that can be read from it. The caller can // decide how many of those bytes can be removed // by passing the count to the read_ack function. // If an error occurred inside the queue, this // function returns an empty view. // // Note that the calls to read_buf and read_ack // may be far apart. Other operations won't interfere // with the read. The only rule is you can't call // read_buf multiple times before calling read_ack. ByteView byte_queue_read_buf(ByteQueue *queue); void byte_queue_read_ack(ByteQueue *queue, uint32_t num); // Similar to the read_buf/read_ack functions, // but write_buf returns a view of the unused // memory inside the queue, and write_ack is // used to tell the queue how many bytes were // written into it. Note that to ensure there // is a minimum amount of free space in the queue, // the user needs to call byte_queue_setmincap. // If an error occurred inside the queue, this // function returns an empty view. // // Note that the calls to write_buf and write_ack // may be far apart. Other operations won't interfere // with the write (except for other byte_queue_write_* // functions). The only rule is you can't call // write_buf multiple times before calling write_ack. ByteView byte_queue_write_buf(ByteQueue *queue); void byte_queue_write_ack(ByteQueue *queue, uint32_t num); // Sets the minimum capacity for the next write // operation and returns 1 if the content of the // queue was moved, else 0 is returned. // // You must not call this function while a write // is pending. In other words, you must do this: // // byte_queue_write_setmincap(queue, mincap); // dst = byte_queue_write_buf(queue, &cap); // ... // byte_queue_write_ack(num); // // And NOT this: // // dst = byte_queue_write_buf(queue); // byte_queue_write_setmincap(queue, mincap); <-- BAD // ... // byte_queue_write_ack(num); // int byte_queue_write_setmincap(ByteQueue *queue, uint32_t mincap); // Write some bytes to the queue. This is a // short hand for write_buf/memcpy/write_ack void byte_queue_write(ByteQueue *queue, void *ptr, uint32_t len); // Write the result of the format into the queue void byte_queue_write_fmt(ByteQueue *queue, const char *fmt, ...); // Write the result of the format into the queue void byte_queue_write_fmt2(ByteQueue *queue, const char *fmt, va_list args); // Returns the current offset inside the queue ByteQueueOffset byte_queue_offset(ByteQueue *queue); // Writes some bytes at the specified offset. It's // the responsibility of the user to make sure that // the offset still refers to content inside the queue. void byte_queue_patch(ByteQueue *queue, ByteQueueOffset off, void *src, uint32_t len); // Returns the number of bytes from the given offset // to the end of the queue. uint32_t byte_queue_size_from_offset(ByteQueue *queue, ByteQueueOffset off); // Removes all bytes from the given offset to the the // end of the queue. void byte_queue_remove_from_offset(ByteQueue *queue, ByteQueueOffset offset);