| /** @file |
| |
| I/O classes |
| |
| @section license License |
| |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you 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. |
| |
| @section watermark Watermark |
| |
| Watermarks can be used as an interface between the data transferring |
| layer (VConnection) and the user layer (a state machine). Watermarks |
| should be used when you need to have at least a certain amount of data |
| to make some determination. For example, when parsing a string, one |
| might wish to ensure that an entire line will come in before consuming |
| the data. In such a case, the water_mark should be set to the largest |
| possible size of the string. (appropriate error handling should take |
| care of excessively long strings). |
| |
| In all other cases, especially when all data will be consumed, the |
| water_mark should be set to 0 (the default). |
| |
| */ |
| |
| #pragma once |
| #define I_IOBuffer_h |
| |
| #include "tscore/ink_platform.h" |
| #include "tscore/ink_apidefs.h" |
| #include "tscore/Allocator.h" |
| #include "tscore/Ptr.h" |
| #include "tscore/ink_assert.h" |
| #include "tscore/ink_resource.h" |
| |
| struct MIOBufferAccessor; |
| |
| class MIOBuffer; |
| class IOBufferReader; |
| class VIO; |
| |
| enum AllocType { |
| NO_ALLOC, |
| MEMALIGNED, |
| DEFAULT_ALLOC, |
| }; |
| |
| #define DEFAULT_BUFFER_NUMBER 128 |
| #define DEFAULT_HUGE_BUFFER_NUMBER 32 |
| #define MAX_MIOBUFFER_READERS 5 |
| #define DEFAULT_BUFFER_ALIGNMENT 8192 // should be disk/page size |
| #define DEFAULT_BUFFER_BASE_SIZE 128 |
| |
| //////////////////////////////////////////////// |
| // These are defines so that code that used 2 // |
| // for buffer size index when 2 was 2K will // |
| // still work if it uses BUFFER_SIZE_INDEX_2K // |
| // instead. // |
| //////////////////////////////////////////////// |
| #define BUFFER_SIZE_INDEX_128 0 |
| #define BUFFER_SIZE_INDEX_256 1 |
| #define BUFFER_SIZE_INDEX_512 2 |
| #define BUFFER_SIZE_INDEX_1K 3 |
| #define BUFFER_SIZE_INDEX_2K 4 |
| #define BUFFER_SIZE_INDEX_4K 5 |
| #define BUFFER_SIZE_INDEX_8K 6 |
| #define BUFFER_SIZE_INDEX_16K 7 |
| #define BUFFER_SIZE_INDEX_32K 8 |
| #define BUFFER_SIZE_INDEX_64K 9 |
| #define BUFFER_SIZE_INDEX_128K 10 |
| #define BUFFER_SIZE_INDEX_256K 11 |
| #define BUFFER_SIZE_INDEX_512K 12 |
| #define BUFFER_SIZE_INDEX_1M 13 |
| #define BUFFER_SIZE_INDEX_2M 14 |
| #define MAX_BUFFER_SIZE_INDEX 14 |
| #define DEFAULT_BUFFER_SIZES (MAX_BUFFER_SIZE_INDEX + 1) |
| |
| #define BUFFER_SIZE_FOR_INDEX(_i) (DEFAULT_BUFFER_BASE_SIZE * (1 << (_i))) |
| #define DEFAULT_SMALL_BUFFER_SIZE BUFFER_SIZE_INDEX_512 |
| #define DEFAULT_LARGE_BUFFER_SIZE BUFFER_SIZE_INDEX_4K |
| #define DEFAULT_TS_BUFFER_SIZE BUFFER_SIZE_INDEX_8K |
| #define DEFAULT_MAX_BUFFER_SIZE BUFFER_SIZE_FOR_INDEX(MAX_BUFFER_SIZE_INDEX) |
| #define MIN_IOBUFFER_SIZE BUFFER_SIZE_INDEX_128 |
| #define MAX_IOBUFFER_SIZE (DEFAULT_BUFFER_SIZES - 1) |
| |
| #define BUFFER_SIZE_ALLOCATED(_i) (BUFFER_SIZE_INDEX_IS_FAST_ALLOCATED(_i) || BUFFER_SIZE_INDEX_IS_XMALLOCED(_i)) |
| |
| #define BUFFER_SIZE_NOT_ALLOCATED DEFAULT_BUFFER_SIZES |
| #define BUFFER_SIZE_INDEX_IS_XMALLOCED(_size_index) (_size_index < 0) |
| #define BUFFER_SIZE_INDEX_IS_FAST_ALLOCATED(_size_index) (((uint64_t)_size_index) < DEFAULT_BUFFER_SIZES) |
| #define BUFFER_SIZE_INDEX_IS_CONSTANT(_size_index) (_size_index >= DEFAULT_BUFFER_SIZES) |
| |
| #define BUFFER_SIZE_FOR_XMALLOC(_size) (-(_size)) |
| #define BUFFER_SIZE_INDEX_FOR_XMALLOC_SIZE(_size) (-(_size)) |
| |
| #define BUFFER_SIZE_FOR_CONSTANT(_size) (_size - DEFAULT_BUFFER_SIZES) |
| #define BUFFER_SIZE_INDEX_FOR_CONSTANT_SIZE(_size) (_size + DEFAULT_BUFFER_SIZES) |
| |
| inkcoreapi extern Allocator ioBufAllocator[DEFAULT_BUFFER_SIZES]; |
| |
| void init_buffer_allocators(int iobuffer_advice); |
| |
| /** |
| A reference counted wrapper around fast allocated or malloced memory. |
| The IOBufferData class provides two basic services around a portion |
| of allocated memory. |
| |
| First, it is a reference counted object and ... |
| |
| @remarks The AllocType enum, is used to define the type of allocation |
| for the memory this IOBufferData object manages. |
| |
| <table> |
| <tr> |
| <td align="center">AllocType</td> |
| <td align="center">Meaning</td> |
| </tr> |
| <tr> |
| <td>NO_ALLOC</td> |
| <td></td> |
| </tr> |
| <tr> |
| <td>MEMALIGNED</td> |
| <td></td> |
| </tr> |
| <tr> |
| <td>DEFAULT_ALLOC</td> |
| <td></td> |
| </tr> |
| </table> |
| |
| */ |
| class IOBufferData : public RefCountObj |
| { |
| public: |
| /** |
| The size of the memory allocated by this IOBufferData. Calculates |
| the amount of memory allocated by this IOBufferData. |
| |
| @return number of bytes allocated for the '_data' member. |
| |
| */ |
| int64_t block_size(); |
| |
| /** |
| Frees the memory managed by this IOBufferData. Deallocates the |
| memory previously allocated by this IOBufferData object. It frees |
| the memory pointed to by '_data' according to the '_mem_type' and |
| '_size_index' members. |
| |
| */ |
| void dealloc(); |
| |
| /** |
| Allocates memory and sets this IOBufferData to point to it. |
| Allocates memory according to the size_index and type |
| parameters. Any previously allocated memory pointed to by |
| this IOBufferData is deallocated. |
| |
| @param size_index |
| @param type of allocation to use; see remarks section. |
| */ |
| void alloc(int64_t size_index, AllocType type = DEFAULT_ALLOC); |
| |
| /** |
| Provides access to the allocated memory. Returns the address of the |
| allocated memory handled by this IOBufferData. |
| |
| @return address of the memory handled by this IOBufferData. |
| |
| */ |
| char * |
| data() |
| { |
| return _data; |
| } |
| |
| /** |
| Cast operator. Provided as a convenience, the cast to a char* applied |
| to the IOBufferData returns the address of the memory handled by the |
| IOBuffer data. In this manner, objects of this class can be used as |
| parameter to functions requiring a char*. |
| |
| */ |
| operator char *() { return _data; } |
| /** |
| Frees the IOBufferData object and its underlying memory. Deallocates |
| the memory managed by this IOBufferData and then frees itself. You |
| should not use this object or reference after this call. |
| |
| */ |
| void free() override; |
| |
| int64_t _size_index; |
| |
| /** |
| Type of allocation used for the managed memory. Stores the type of |
| allocation used for the memory currently managed by the IOBufferData |
| object. Do not set or modify this value directly. Instead use the |
| alloc or dealloc methods. |
| |
| */ |
| AllocType _mem_type = NO_ALLOC; |
| |
| /** |
| Points to the allocated memory. This member stores the address of |
| the allocated memory. You should not modify its value directly, |
| instead use the alloc or dealloc methods. |
| |
| */ |
| char *_data = nullptr; |
| |
| const char *_location = nullptr; |
| |
| /** |
| Constructor. Initializes state for a IOBufferData object. Do not use |
| this method. Use one of the functions with the 'new_' prefix instead. |
| |
| */ |
| IOBufferData() : _size_index(BUFFER_SIZE_NOT_ALLOCATED) {} |
| |
| // noncopyable, declaration only |
| IOBufferData(const IOBufferData &) = delete; |
| IOBufferData &operator=(const IOBufferData &) = delete; |
| }; |
| |
| inkcoreapi extern ClassAllocator<IOBufferData> ioDataAllocator; |
| |
| /** |
| A linkable portion of IOBufferData. IOBufferBlock is a chainable |
| buffer block descriptor. The IOBufferBlock represents both the used |
| and available space in the underlying block. The IOBufferBlock is not |
| sharable between buffers but rather represents what part of the data |
| block is both in use and usable by the MIOBuffer it is attached to. |
| |
| */ |
| class IOBufferBlock : public RefCountObj |
| { |
| public: |
| /** |
| Access the actual data. Provides access to rhe underlying data |
| managed by the IOBufferData. |
| |
| @return pointer to the underlying data. |
| |
| */ |
| char * |
| buf() |
| { |
| return data->_data; |
| } |
| |
| /** |
| Beginning of the inuse section. Returns the position in the buffer |
| where the inuse area begins. |
| |
| @return pointer to the start of the inuse section. |
| |
| */ |
| char * |
| start() |
| { |
| return _start; |
| } |
| |
| /** |
| End of the used space. Returns a pointer to end of the used space |
| in the data buffer represented by this block. |
| |
| @return pointer to the end of the inuse portion of the block. |
| |
| */ |
| char * |
| end() |
| { |
| return _end; |
| } |
| |
| /** |
| End of the data buffer. Returns a pointer to end of the data buffer |
| represented by this block. |
| |
| */ |
| char * |
| buf_end() |
| { |
| return _buf_end; |
| } |
| |
| /** |
| Size of the inuse area. Returns the size of the current inuse area. |
| |
| @return bytes occupied by the inuse area. |
| |
| */ |
| int64_t |
| size() |
| { |
| return (int64_t)(_end - _start); |
| } |
| |
| /** |
| Size of the data available for reading. Returns the size of the data |
| available for reading in the inuse area. |
| |
| @return bytes available for reading from the inuse area. |
| |
| */ |
| int64_t |
| read_avail() const |
| { |
| return (int64_t)(_end - _start); |
| } |
| |
| /** |
| Space available in the buffer. Returns the number of bytes that can |
| be written to the data buffer. |
| |
| @return space available for writing in this IOBufferBlock. |
| */ |
| int64_t |
| write_avail() |
| { |
| return (int64_t)(_buf_end - _end); |
| } |
| |
| /** |
| Size of the memory allocated by the underlying IOBufferData. |
| Computes the size of the entire block, which includes the used and |
| available areas. It is the memory allocated by the IOBufferData |
| referenced by this IOBufferBlock. |
| |
| @return bytes allocated to the IOBufferData referenced by this |
| IOBufferBlock. |
| |
| */ |
| int64_t |
| block_size() |
| { |
| return data->block_size(); |
| } |
| |
| /** |
| Decrease the size of the inuse area. Moves forward the start of |
| the inuse area. This also decreases the number of available bytes |
| for reading. |
| |
| @param len bytes to consume or positions to skip for the start of |
| the inuse area. |
| |
| */ |
| void consume(int64_t len); |
| |
| /** |
| Increase the inuse area of the block. Adds 'len' bytes to the inuse |
| area of the block. Data should be copied into the data buffer by |
| using end() to find the start of the free space in the data buffer |
| before calling fill() |
| |
| @param len bytes to increase the inuse area. It must be less than |
| or equal to the value of write_avail(). |
| |
| */ |
| void fill(int64_t len); |
| |
| /** |
| Reset the inuse area. The start and end of the inuse area are reset |
| but the actual IOBufferData referenced by this IOBufferBlock is not |
| modified. This effectively reduces the number of bytes available |
| for reading to zero, and the number of bytes available for writing |
| to the size of the entire buffer. |
| |
| */ |
| void reset(); |
| |
| /** |
| Create a copy of the IOBufferBlock. Creates and returns a copy of this |
| IOBufferBlock that references the same data that this IOBufferBlock |
| (it does not allocate an another buffer). The cloned block will not |
| have a writable space since the original IOBufferBlock maintains the |
| ownership for writing data to the block. |
| |
| @return copy of this IOBufferBlock. |
| |
| */ |
| IOBufferBlock *clone() const; |
| |
| /** |
| Clear the IOBufferData this IOBufferBlock handles. Clears this |
| IOBufferBlock's reference to the data buffer (IOBufferData). You can |
| use alloc after this call to allocate an IOBufferData associated to |
| this IOBufferBlock. |
| |
| */ |
| void clear(); |
| |
| /** |
| Allocate a data buffer. Allocates a data buffer for this IOBufferBlock |
| based on index 'i'. Index values are described in the remarks |
| section in MIOBuffer. |
| |
| */ |
| void alloc(int64_t i); |
| |
| /** |
| Clear the IOBufferData this IOBufferBlock handles. Clears this |
| IOBufferBlock's reference to the data buffer (IOBufferData). |
| |
| */ |
| void dealloc(); |
| |
| /** |
| Set or replace this IOBufferBlock's IOBufferData member. Sets this |
| IOBufferBlock's IOBufferData member to point to the IOBufferData |
| passed in. You can optionally specify the inuse area with the 'len' |
| argument and an offset for the start. |
| |
| @param d new IOBufferData this IOBufferBlock references. |
| @param len in use area to set. It must be less than or equal to the |
| length of the block size *IOBufferData). |
| @param offset bytes to skip from the beginning of the IOBufferData |
| and to mark its start. |
| |
| */ |
| void set(IOBufferData *d, int64_t len = 0, int64_t offset = 0); |
| void set_internal(void *b, int64_t len, int64_t asize_index); |
| |
| /** |
| Frees the IOBufferBlock object and its underlying memory. |
| Removes the reference to the IOBufferData object and then frees |
| itself. You should not use this object or reference after this |
| call. |
| |
| */ |
| void free() override; |
| |
| char *_start = nullptr; |
| char *_end = nullptr; |
| char *_buf_end = nullptr; |
| |
| const char *_location = nullptr; |
| |
| /** |
| The underlying reference to the allocated memory. A reference to a |
| IOBufferData representing the memory allocated to this buffer. Do |
| not set or modify its value directly. |
| |
| */ |
| Ptr<IOBufferData> data; |
| |
| /** |
| Reference to another IOBufferBlock. A reference to another |
| IOBufferBlock that allows this object to link to other. |
| |
| */ |
| Ptr<IOBufferBlock> next; |
| |
| /** |
| Constructor of a IOBufferBlock. Do not use it to create a new object, |
| instead call new_IOBufferBlock |
| |
| */ |
| IOBufferBlock(); |
| |
| // noncopyable |
| IOBufferBlock(const IOBufferBlock &) = delete; |
| IOBufferBlock &operator=(const IOBufferBlock &) = delete; |
| }; |
| |
| extern inkcoreapi ClassAllocator<IOBufferBlock> ioBlockAllocator; |
| |
| /** A class for holding a chain of IO buffer blocks. |
| This class is intended to be used as a member variable for other classes that |
| need to anchor an IO Buffer chain but don't need the full @c MIOBuffer machinery. |
| That is, the owner is the only reader/writer of the data. |
| |
| This does not handle incremental reads or writes well. The intent is that data is |
| placed in the instance, held for a while, then used and discarded. |
| |
| @note Contrast also with @c IOBufferReader which is similar but requires an |
| @c MIOBuffer as its owner. |
| */ |
| class IOBufferChain |
| { |
| using self_type = IOBufferChain; ///< Self reference type. |
| |
| public: |
| /// Default constructor - construct empty chain. |
| IOBufferChain() = default; |
| /// Shallow copy. |
| self_type &operator=(self_type const &that); |
| |
| /// Shallow append. |
| self_type &operator+=(self_type const &that); |
| |
| /// Number of bytes of content. |
| int64_t length() const; |
| |
| /// Copy a chain of @a blocks in to this object up to @a length bytes. |
| /// If @a offset is greater than 0 that many bytes are skipped. Those bytes do not count |
| /// as part of @a length. |
| /// This creates a new chain using existing data blocks. This |
| /// breaks the original chain so that changes there (such as appending blocks) |
| /// is not reflected in this chain. |
| /// @return The number of bytes written to the chain. |
| int64_t write(IOBufferBlock *blocks, int64_t length, int64_t offset = 0); |
| |
| /// Add the content of a buffer block. |
| /// The buffer block is unchanged. |
| int64_t write(IOBufferData *data, int64_t length = 0, int64_t offset = 0); |
| |
| /// Remove @a size bytes of content from the front of the chain. |
| /// @return The actual number of bytes removed. |
| int64_t consume(int64_t size); |
| |
| /// Clear current chain. |
| void clear(); |
| |
| /// Get the first block. |
| IOBufferBlock *head(); |
| IOBufferBlock const *head() const; |
| |
| /// STL Container support. |
| |
| /// Block iterator. |
| /// @internal The reason for this is to override the increment operator. |
| class const_iterator : public std::forward_iterator_tag |
| { |
| using self_type = const_iterator; ///< Self reference type. |
| protected: |
| /// Current buffer block. |
| IOBufferBlock *_b = nullptr; |
| |
| public: |
| using value_type = const IOBufferBlock; ///< Iterator value type. |
| |
| const_iterator() = default; ///< Default constructor. |
| |
| /// Copy constructor. |
| // cppcheck-suppress noExplicitConstructor; copy constructor |
| const_iterator(self_type const &that); |
| |
| /// Assignment. |
| self_type &operator=(self_type const &that); |
| |
| /// Equality. |
| bool operator==(self_type const &that) const; |
| /// Inequality. |
| bool operator!=(self_type const &that) const; |
| |
| value_type &operator*() const; |
| value_type *operator->() const; |
| |
| self_type &operator++(); |
| self_type operator++(int); |
| }; |
| |
| class iterator : public const_iterator |
| { |
| using self_type = iterator; ///< Self reference type. |
| public: |
| using value_type = IOBufferBlock; ///< Dereferenced type. |
| |
| value_type &operator*() const; |
| value_type *operator->() const; |
| }; |
| |
| using value_type = IOBufferBlock; |
| |
| iterator begin(); |
| const_iterator begin() const; |
| |
| iterator end(); |
| const_iterator end() const; |
| |
| protected: |
| /// Append @a block. |
| void append(IOBufferBlock *block); |
| |
| /// Head of buffer block chain. |
| Ptr<IOBufferBlock> _head; |
| /// Tail of the block chain. |
| IOBufferBlock *_tail = nullptr; |
| /// The amount of data of interest. |
| /// Not necessarily the amount of data in the chain of blocks. |
| int64_t _len = 0; |
| }; |
| |
| /** |
| An independent reader from an MIOBuffer. A reader for a set of |
| IOBufferBlocks. The IOBufferReader represents the place where a given |
| consumer of buffer data is reading from. It provides a uniform interface |
| for easily accessing the data contained in a list of IOBufferBlocks |
| associated with the IOBufferReader. |
| |
| IOBufferReaders are the abstraction that determine when data blocks |
| can be removed from the buffer. |
| |
| */ |
| class IOBufferReader |
| { |
| public: |
| /** |
| Start of unconsumed data. Returns a pointer to first unconsumed data |
| on the buffer for this reader. A null pointer indicates no data is |
| available. It uses the current start_offset value. |
| |
| @return pointer to the start of the unconsumed data. |
| |
| */ |
| char *start(); |
| |
| /** |
| End of inuse area of the first block with unconsumed data. Returns a |
| pointer to the end of the first block with unconsumed data for this |
| reader. A nullptr pointer indicates there are no blocks with unconsumed |
| data for this reader. |
| |
| @return pointer to the end of the first block with unconsumed data. |
| |
| */ |
| char *end(); |
| |
| /** |
| Amount of data available across all of the IOBufferBlocks. Returns the |
| number of unconsumed bytes of data available to this reader across |
| all remaining IOBufferBlocks. It subtracts the current start_offset |
| value from the total. |
| |
| @return bytes of data available across all the buffers. |
| |
| */ |
| int64_t read_avail(); |
| |
| /** Check if there is more than @a size bytes available to read. |
| @return @c true if more than @a size byte are available. |
| */ |
| bool is_read_avail_more_than(int64_t size); |
| |
| /** |
| Number of IOBufferBlocks with data in the block list. Returns the |
| number of IOBufferBlocks on the block list with data remaining for |
| this reader. |
| |
| @return number of blocks with data for this reader. |
| |
| */ |
| int block_count(); |
| |
| /** |
| Amount of data available in the first buffer with data for this |
| reader. Returns the number of unconsumed bytes of data available |
| on the first IOBufferBlock with data for this reader. |
| |
| @return number of unconsumed bytes of data available in the first |
| buffer. |
| |
| */ |
| int64_t block_read_avail(); |
| |
| /** Get a view of the data available to read. |
| * |
| * @return A view encompassing currently available readable data. |
| */ |
| std::string_view block_read_view(); |
| |
| void skip_empty_blocks(); |
| |
| /** |
| Clears all fields in this IOBuffeReader, rendering it unusable. Drops |
| the reference to the IOBufferBlock list, the accessor, MIOBuffer and |
| resets this reader's state. You have to set those fields in order |
| to use this object again. |
| |
| */ |
| void clear(); |
| |
| /** |
| Instruct the reader to reset the IOBufferBlock list. Resets the |
| reader to the point to the start of the block where new data will |
| be written. After this call, the start_offset field is set to zero |
| and the list of IOBufferBlocks is set using the associated MIOBuffer. |
| |
| */ |
| void reset(); |
| |
| /** |
| Consume a number of bytes from this reader's IOBufferBlock |
| list. Advances the current position in the IOBufferBlock list of |
| this reader by n bytes. |
| |
| @param n number of bytes to consume. It must be less than or equal |
| to read_avail(). |
| |
| */ |
| void consume(int64_t n); |
| |
| /** |
| Create another reader with access to the same data as this |
| IOBufferReader. Allocates a new reader with the same state as this |
| IOBufferReader. This means that the new reader will point to the same |
| list of IOBufferBlocks and to the same buffer position as this reader. |
| |
| @return new reader with the same state as this. |
| |
| */ |
| IOBufferReader *clone(); |
| |
| /** |
| Deallocate this reader. Removes and deallocates this reader from |
| the underlying MIOBuffer. This IOBufferReader object must not be |
| used after this call. |
| |
| */ |
| void dealloc(); |
| |
| /** |
| Get a pointer to the first block with data. Returns a pointer to |
| the first IOBufferBlock in the block chain with data available for |
| this reader |
| |
| @return pointer to the first IOBufferBlock in the list with data |
| available for this reader. |
| |
| */ |
| IOBufferBlock *get_current_block(); |
| |
| /** |
| Consult this reader's MIOBuffer writable space. Queries the MIOBuffer |
| associated with this reader about the amount of writable space |
| available without adding any blocks on the buffer and returns true |
| if it is less than the water mark. |
| |
| @return true if the MIOBuffer associated with this IOBufferReader |
| returns true in MIOBuffer::current_low_water(). |
| |
| */ |
| bool current_low_water(); |
| |
| /** |
| Queries the underlying MIOBuffer about. Returns true if the amount |
| of writable space after adding a block on the underlying MIOBuffer |
| is less than its water mark. This function call may add blocks to |
| the MIOBuffer (see MIOBuffer::low_water()). |
| |
| @return result of MIOBuffer::low_water() on the MIOBuffer for |
| this reader. |
| |
| */ |
| bool low_water(); |
| |
| /** |
| To see if the amount of data available to the reader is greater than |
| the MIOBuffer's water mark. Indicates whether the amount of data |
| available to this reader exceeds the water mark for this reader's |
| MIOBuffer. |
| |
| @return true if the amount of data exceeds the MIOBuffer's water mark. |
| |
| */ |
| bool high_water(); |
| |
| /** |
| Perform a memchr() across the list of IOBufferBlocks. Returns the |
| offset from the current start point of the reader to the first |
| occurrence of character 'c' in the buffer. |
| |
| @param c character to look for. |
| @param len number of characters to check. If len exceeds the number |
| of bytes available on the buffer or INT64_MAX is passed in, the |
| number of bytes available to the reader is used. It is independent |
| of the offset value. |
| @param offset number of the bytes to skip over before beginning |
| the operation. |
| @return -1 if c is not found, otherwise position of the first |
| occurrence. |
| |
| */ |
| inkcoreapi int64_t memchr(char c, int64_t len = INT64_MAX, int64_t offset = 0); |
| |
| /** |
| Copies and consumes data. Copies len bytes of data from the buffer |
| into the supplied buffer, which must be allocated prior to the call |
| and it must be at large enough for the requested bytes. Once the |
| data is copied, it consumed from the reader. |
| |
| @param buf in which to place the data. |
| @param len bytes to copy and consume. If 'len' exceeds the bytes |
| available to the reader, the number of bytes available is used |
| instead. |
| |
| @return number of bytes copied and consumed. |
| |
| */ |
| inkcoreapi int64_t read(void *buf, int64_t len); |
| |
| /** |
| Copy data but do not consume it. Copies 'len' bytes of data from |
| the current buffer into the supplied buffer. The copy skips the |
| number of bytes specified by 'offset' beyond the current point of |
| the reader. It also takes into account the current start_offset value. |
| |
| @param buf in which to place the data. The pointer is modified after |
| the call and points one position after the end of the data copied. |
| @param len bytes to copy. If len exceeds the bytes available to the |
| reader or INT64_MAX is passed in, the number of bytes available is |
| used instead. No data is consumed from the reader in this operation. |
| @param offset bytes to skip from the current position. The parameter |
| is modified after the call. |
| @return pointer to one position after the end of the data copied. The |
| parameter buf is set to this value also. |
| |
| */ |
| inkcoreapi char *memcpy(void *buf, int64_t len = INT64_MAX, int64_t offset = 0); |
| |
| /** |
| Subscript operator. Returns a reference to the character at the |
| specified position. You must ensure that it is within an appropriate |
| range. |
| |
| @param i positions beyond the current point of the reader. It must |
| be less than the number of the bytes available to the reader. |
| |
| @return reference to the character in that position. |
| |
| */ |
| char &operator[](int64_t i); |
| |
| MIOBuffer * |
| writer() const |
| { |
| return mbuf; |
| } |
| MIOBuffer * |
| allocated() const |
| { |
| return mbuf; |
| } |
| |
| MIOBufferAccessor *accessor = nullptr; // pointer back to the accessor |
| |
| /** |
| Back pointer to this object's MIOBuffer. A pointer back to the |
| MIOBuffer this reader is allocated from. |
| |
| */ |
| MIOBuffer *mbuf = nullptr; |
| Ptr<IOBufferBlock> block; |
| |
| /** |
| Offset beyond the shared start(). The start_offset is used in the |
| calls that copy or consume data and is an offset at the beginning |
| of the available data. |
| |
| */ |
| int64_t start_offset = 0; |
| int64_t size_limit = INT64_MAX; |
| |
| IOBufferReader() {} |
| }; |
| |
| /** |
| A multiple reader, single writer memory buffer. MIOBuffers are at |
| the center of all IOCore data transfer. MIOBuffers are the data |
| buffers used to transfer data to and from VConnections. A MIOBuffer |
| points to a list of IOBufferBlocks which in turn point to IOBufferData |
| structures that in turn point to the actual data. MIOBuffer allows one |
| producer and multiple consumers. The buffer fills up according the |
| amount of data outstanding for the slowest consumer. Thus, MIOBuffer |
| implements automatic flow control between readers of different speeds. |
| Data on IOBuffer is immutable. Once written it cannot be modified, only |
| deallocated once all consumers have finished with it. Immutability is |
| necessary since data can be shared between buffers, which means that |
| multiple IOBufferBlock objects may reference the same data but only |
| one will have ownership for writing. |
| |
| */ |
| class MIOBuffer |
| { |
| public: |
| /** |
| Increase writer's inuse area. Instructs the writer associated with |
| this MIOBuffer to increase the inuse area of the block by as much as |
| 'len' bytes. |
| |
| @param len number of bytes to add to the inuse area of the block. |
| |
| */ |
| void fill(int64_t len); |
| |
| /** |
| Adds a block to the end of the block list. The block added to list |
| must be writable by this buffer and must not be writable by any |
| other buffer. |
| |
| */ |
| void append_block(IOBufferBlock *b); |
| |
| /** |
| Adds a new block to the end of the block list. The size is determined |
| by asize_index. See the remarks section for a mapping of indexes to |
| buffer block sizes. |
| |
| */ |
| void append_block(int64_t asize_index); |
| |
| /** |
| Adds a new block to the end of the block list. Note that this does nothing when the next block of the current writer exists. |
| The block size is the same as specified size when the buffer was allocated. |
| */ |
| void add_block(); |
| |
| /** |
| Deprecated |
| |
| TODO: remove this function. Because ats_xmalloc() doesn't exist anymore. |
| |
| Adds by reference len bytes of data pointed to by b to the end |
| of the buffer. b MUST be a pointer to the beginning of block |
| allocated from the ats_xmalloc() routine. The data will be deallocated |
| by the buffer once all readers on the buffer have consumed it. |
| |
| */ |
| void append_xmalloced(void *b, int64_t len); |
| |
| /** |
| Adds by reference len bytes of data pointed to by b to the end of the |
| buffer. b MUST be a pointer to the beginning of block allocated from |
| ioBufAllocator of the corresponding index for fast_size_index. The |
| data will be deallocated by the buffer once all readers on the buffer |
| have consumed it. |
| |
| */ |
| void append_fast_allocated(void *b, int64_t len, int64_t fast_size_index); |
| |
| /** |
| Adds the nbytes worth of data pointed by rbuf to the buffer. The |
| data is copied into the buffer. write() does not respect watermarks |
| or buffer size limits. Users of write must implement their own flow |
| control. Returns the number of bytes added. |
| |
| */ |
| inkcoreapi int64_t write(const void *rbuf, int64_t nbytes); |
| |
| /** |
| Add by data from IOBufferReader r to the this buffer by reference. If |
| len is INT64_MAX, all available data on the reader is added. If len is |
| less than INT64_MAX, the smaller of len or the amount of data on the |
| buffer is added. If offset is greater than zero, than the offset |
| bytes of data at the front of the reader are skipped. Bytes skipped |
| by offset reduce the number of bytes available on the reader used |
| in the amount of data to add computation. write() does not respect |
| watermarks or buffer size limits. Users of write must implement |
| their own flow control. Returns the number of bytes added. Each |
| write() call creates a new IOBufferBlock, even if it is for one |
| byte. As such, it's necessary to exercise caution in any code that |
| repeatedly transfers data from one buffer to another, especially if |
| the data is being read over the network as it may be coming in very |
| small chunks. Because deallocation of outstanding buffer blocks is |
| recursive, it's possible to overrun the stack if too many blocks |
| have been added to the buffer chain. It's imperative that users |
| both implement their own flow control to prevent too many bytes |
| from becoming outstanding on a buffer that the write() call is |
| being used and that care be taken to ensure the transfers are of a |
| minimum size. Should it be necessary to make a large number of small |
| transfers, it's preferable to use a interface that copies the data |
| rather than sharing blocks to prevent a build of blocks on the buffer. |
| |
| */ |
| inkcoreapi int64_t write(IOBufferReader *r, int64_t len = INT64_MAX, int64_t offset = 0); |
| |
| /** Copy data from the @a chain to this buffer. |
| New IOBufferBlocks are allocated so this gets a copy of the data that is independent of the source. |
| @a offset bytes are skipped at the start of the @a chain. The length is bounded by @a len and the |
| size in the @a chain. |
| |
| @return the number of bytes copied. |
| |
| @internal I do not like counting @a offset against @a bytes but that's how @c write works... |
| */ |
| int64_t write(IOBufferChain const *chain, int64_t len = INT64_MAX, int64_t offset = 0); |
| |
| /** |
| Returns a pointer to the first writable block on the block chain. |
| Returns nullptr if there are not currently any writable blocks on the |
| block list. |
| */ |
| IOBufferBlock * |
| first_write_block() |
| { |
| if (_writer) { |
| if (_writer->next && !_writer->write_avail()) { |
| return _writer->next.get(); |
| } |
| ink_assert(!_writer->next || !_writer->next->read_avail()); |
| return _writer.get(); |
| } |
| |
| return nullptr; |
| } |
| |
| char * |
| buf() |
| { |
| IOBufferBlock *b = first_write_block(); |
| return b ? b->buf() : nullptr; |
| } |
| |
| char * |
| buf_end() |
| { |
| return first_write_block()->buf_end(); |
| } |
| |
| char * |
| start() |
| { |
| return first_write_block()->start(); |
| } |
| |
| char * |
| end() |
| { |
| return first_write_block()->end(); |
| } |
| |
| /** |
| Returns the amount of space of available for writing on the first |
| writable block on the block chain (the one that would be returned |
| by first_write_block()). |
| |
| */ |
| int64_t block_write_avail(); |
| |
| /** |
| Returns the amount of space of available for writing on all writable |
| blocks currently on the block chain. Will NOT add blocks to the |
| block chain. |
| |
| */ |
| int64_t current_write_avail(); |
| |
| /** |
| Adds blocks for writing if the watermark criteria are met. Returns |
| the amount of space of available for writing on all writable blocks |
| on the block chain after a block due to the watermark criteria. |
| |
| */ |
| int64_t write_avail(); |
| |
| /** |
| Returns the default data block size for this buffer. |
| |
| */ |
| int64_t block_size(); |
| |
| /** |
| Returns true if amount of the data outstanding on the buffer exceeds |
| the watermark. |
| |
| */ |
| bool |
| high_water() |
| { |
| return is_max_read_avail_more_than(this->water_mark); |
| } |
| |
| /** |
| Returns true if the amount of writable space after adding a block on |
| the buffer is less than the water mark. Since this function relies |
| on write_avail() it may add blocks. |
| |
| */ |
| bool |
| low_water() |
| { |
| return write_avail() <= water_mark; |
| } |
| |
| /** |
| Returns true if amount the amount writable space without adding and |
| blocks on the buffer is less than the water mark. |
| |
| */ |
| bool |
| current_low_water() |
| { |
| return current_write_avail() <= water_mark; |
| } |
| |
| /** |
| Allocates a new IOBuffer reader and sets it's its 'accessor' field |
| to point to 'anAccessor'. |
| |
| */ |
| IOBufferReader *alloc_accessor(MIOBufferAccessor *anAccessor); |
| |
| /** |
| Allocates an IOBufferReader for this buffer. IOBufferReaders hold |
| data on the buffer for different consumers. IOBufferReaders are |
| REQUIRED when using buffer. alloc_reader() MUST ONLY be a called |
| on newly allocated buffers. Calling on a buffer with data already |
| placed on it will result in the reader starting at an indeterminate |
| place on the buffer. |
| |
| */ |
| IOBufferReader *alloc_reader(); |
| |
| /** |
| Allocates a new reader on this buffer and places it's starting |
| point at the same place as reader r. r MUST be a pointer to a reader |
| previous allocated from this buffer. |
| |
| */ |
| IOBufferReader *clone_reader(IOBufferReader *r); |
| |
| /** |
| Deallocates reader e from this buffer. e MUST be a pointer to a reader |
| previous allocated from this buffer. Reader need to allocated when a |
| particularly consumer is being removed from the buffer but the buffer |
| is still in use. Deallocation is not necessary when the buffer is |
| being freed as all outstanding readers are automatically deallocated. |
| |
| */ |
| void dealloc_reader(IOBufferReader *e); |
| |
| /** |
| Deallocates all outstanding readers on the buffer. |
| |
| */ |
| void dealloc_all_readers(); |
| |
| void set(void *b, int64_t len); |
| void alloc(int64_t i); |
| void append_block_internal(IOBufferBlock *b); |
| int64_t write(IOBufferBlock const *b, int64_t len, int64_t offset); |
| |
| // internal interface |
| |
| /** |
| Get the maximum amount of available data across all of the readers. |
| If there're no allocated reader, return available data size of current writer. |
| |
| This calls IOBufferReader::read_avail() and it could be expensive when it has a ton of IOBufferBlock. |
| The `is_max_read_avail(int64_t size)` is preferred if possible. |
| |
| @return maximum amount of available data |
| */ |
| int64_t max_read_avail(); |
| |
| /** |
| Check if there is more than @a size bytes available to read. |
| |
| @return @c true if more than @a size byte are available. |
| */ |
| bool is_max_read_avail_more_than(int64_t size); |
| |
| int max_block_count(); |
| void check_add_block(); |
| |
| IOBufferBlock *get_current_block(); |
| |
| void |
| reset() |
| { |
| if (_writer) { |
| _writer->reset(); |
| } |
| for (auto &reader : readers) { |
| if (reader.allocated()) { |
| reader.reset(); |
| } |
| } |
| } |
| |
| void |
| init_readers() |
| { |
| for (auto &reader : readers) { |
| if (reader.allocated() && !reader.block) { |
| reader.block = _writer; |
| } |
| } |
| } |
| |
| void |
| dealloc() |
| { |
| _writer = nullptr; |
| dealloc_all_readers(); |
| } |
| |
| void |
| clear() |
| { |
| dealloc(); |
| size_index = BUFFER_SIZE_NOT_ALLOCATED; |
| water_mark = 0; |
| } |
| |
| int64_t size_index; |
| |
| /** |
| Determines when to stop writing or reading. The watermark is the |
| level to which the producer (filler) is required to fill the buffer |
| before it can expect the reader to consume any data. A watermark |
| of zero means that the reader will consume any amount of data, |
| no matter how small. |
| |
| */ |
| int64_t water_mark; |
| |
| Ptr<IOBufferBlock> _writer; |
| IOBufferReader readers[MAX_MIOBUFFER_READERS]; |
| |
| const char *_location = nullptr; |
| |
| MIOBuffer(void *b, int64_t bufsize, int64_t aWater_mark); |
| // cppcheck-suppress noExplicitConstructor; allow implicit conversion |
| MIOBuffer(int64_t default_size_index); |
| MIOBuffer(); |
| ~MIOBuffer(); |
| }; |
| |
| /** |
| A wrapper for either a reader or a writer of an MIOBuffer. |
| |
| */ |
| struct MIOBufferAccessor { |
| IOBufferReader * |
| reader() const |
| { |
| return entry; |
| } |
| |
| MIOBuffer * |
| writer() const |
| { |
| return mbuf; |
| } |
| |
| int64_t |
| block_size() const |
| { |
| return mbuf->block_size(); |
| } |
| |
| void reader_for(IOBufferReader *abuf); |
| void reader_for(MIOBuffer *abuf); |
| void writer_for(MIOBuffer *abuf); |
| |
| void |
| clear() |
| { |
| mbuf = nullptr; |
| entry = nullptr; |
| } |
| |
| MIOBufferAccessor() {} |
| |
| ~MIOBufferAccessor(); |
| |
| #ifdef DEBUG |
| const char *name = nullptr; |
| #endif |
| |
| // noncopyable |
| MIOBufferAccessor(const MIOBufferAccessor &) = delete; |
| MIOBufferAccessor &operator=(const MIOBufferAccessor &) = delete; |
| |
| private: |
| MIOBuffer *mbuf = nullptr; |
| IOBufferReader *entry = nullptr; |
| }; |
| |
| extern MIOBuffer *new_MIOBuffer_internal(const char *loc, int64_t size_index); |
| |
| class MIOBuffer_tracker |
| { |
| const char *loc; |
| |
| public: |
| explicit MIOBuffer_tracker(const char *_loc) : loc(_loc) {} |
| MIOBuffer * |
| operator()(int64_t size_index) |
| { |
| return new_MIOBuffer_internal(loc, size_index); |
| } |
| }; |
| |
| extern MIOBuffer *new_empty_MIOBuffer_internal(const char *loc, int64_t size_index); |
| |
| class Empty_MIOBuffer_tracker |
| { |
| const char *loc; |
| |
| public: |
| explicit Empty_MIOBuffer_tracker(const char *_loc) : loc(_loc) {} |
| MIOBuffer * |
| operator()(int64_t size_index) |
| { |
| return new_empty_MIOBuffer_internal(loc, size_index); |
| } |
| }; |
| |
| /// MIOBuffer allocator/deallocator |
| #define new_MIOBuffer MIOBuffer_tracker(RES_PATH("memory/IOBuffer/")) |
| #define new_empty_MIOBuffer Empty_MIOBuffer_tracker(RES_PATH("memory/IOBuffer/")) |
| extern void free_MIOBuffer(MIOBuffer *mio); |
| ////////////////////////////////////////////////////////////////////// |
| |
| extern IOBufferBlock *new_IOBufferBlock_internal(const char *loc); |
| |
| extern IOBufferBlock *new_IOBufferBlock_internal(const char *loc, IOBufferData *d, int64_t len = 0, int64_t offset = 0); |
| |
| class IOBufferBlock_tracker |
| { |
| const char *loc; |
| |
| public: |
| explicit IOBufferBlock_tracker(const char *_loc) : loc(_loc) {} |
| IOBufferBlock * |
| operator()() |
| { |
| return new_IOBufferBlock_internal(loc); |
| } |
| IOBufferBlock * |
| operator()(Ptr<IOBufferData> &d, int64_t len = 0, int64_t offset = 0) |
| { |
| return new_IOBufferBlock_internal(loc, d.get(), len, offset); |
| } |
| }; |
| |
| /// IOBufferBlock allocator |
| #define new_IOBufferBlock IOBufferBlock_tracker(RES_PATH("memory/IOBuffer/")) |
| //////////////////////////////////////////////////////////// |
| |
| extern IOBufferData *new_IOBufferData_internal(const char *location, int64_t size_index, AllocType type = DEFAULT_ALLOC); |
| |
| extern IOBufferData *new_xmalloc_IOBufferData_internal(const char *location, void *b, int64_t size); |
| |
| class IOBufferData_tracker |
| { |
| const char *loc; |
| |
| public: |
| explicit IOBufferData_tracker(const char *_loc) : loc(_loc) {} |
| IOBufferData * |
| operator()(int64_t size_index, AllocType type = DEFAULT_ALLOC) |
| { |
| return new_IOBufferData_internal(loc, size_index, type); |
| } |
| }; |
| |
| // TODO: remove new_xmalloc_IOBufferData. Because ats_xmalloc() doesn't exist anymore. |
| #define new_IOBufferData IOBufferData_tracker(RES_PATH("memory/IOBuffer/")) |
| #define new_xmalloc_IOBufferData(b, size) new_xmalloc_IOBufferData_internal(RES_PATH("memory/IOBuffer/"), (b), (size)) |
| |
| extern int64_t iobuffer_size_to_index(int64_t size, int64_t max); |
| extern int64_t index_to_buffer_size(int64_t idx); |
| /** |
| Clone a IOBufferBlock chain. Used to snarf a IOBufferBlock chain |
| w/o copy. |
| |
| @param b head of source IOBufferBlock chain. |
| @param offset # bytes in the beginning to skip. |
| @param len bytes to copy from source. |
| @return ptr to head of new IOBufferBlock chain. |
| |
| */ |
| extern IOBufferBlock *iobufferblock_clone(IOBufferBlock *b, int64_t offset, int64_t len); |
| /** |
| Skip over specified bytes in chain. Used for dropping references. |
| |
| @param b head of source IOBufferBlock chain. |
| @param poffset originally offset in b, finally offset in returned |
| IOBufferBlock. |
| @param plen value of write is subtracted from plen in the function. |
| @param write bytes to skip. |
| @return ptr to head of new IOBufferBlock chain. |
| |
| */ |
| extern IOBufferBlock *iobufferblock_skip(IOBufferBlock *b, int64_t *poffset, int64_t *plen, int64_t write); |
| |
| inline IOBufferChain & |
| IOBufferChain::operator=(self_type const &that) |
| { |
| _head = that._head; |
| _tail = that._tail; |
| _len = that._len; |
| return *this; |
| } |
| |
| inline IOBufferChain & |
| IOBufferChain::operator+=(self_type const &that) |
| { |
| if (nullptr == _head) |
| *this = that; |
| else { |
| _tail->next = that._head; |
| _tail = that._tail; |
| _len += that._len; |
| } |
| return *this; |
| } |
| |
| inline int64_t |
| IOBufferChain::length() const |
| { |
| return _len; |
| } |
| |
| inline IOBufferBlock const * |
| IOBufferChain::head() const |
| { |
| return _head.get(); |
| } |
| |
| inline IOBufferBlock * |
| IOBufferChain::head() |
| { |
| return _head.get(); |
| } |
| |
| inline void |
| IOBufferChain::clear() |
| { |
| _head = nullptr; |
| _tail = nullptr; |
| _len = 0; |
| } |
| |
| inline IOBufferChain::const_iterator::const_iterator(self_type const &that) : _b(that._b) {} |
| |
| inline IOBufferChain::const_iterator & |
| IOBufferChain::const_iterator::operator=(self_type const &that) |
| { |
| _b = that._b; |
| return *this; |
| } |
| |
| inline bool |
| IOBufferChain::const_iterator::operator==(self_type const &that) const |
| { |
| return _b == that._b; |
| } |
| |
| inline bool |
| IOBufferChain::const_iterator::operator!=(self_type const &that) const |
| { |
| return _b != that._b; |
| } |
| |
| inline IOBufferChain::const_iterator::value_type & |
| IOBufferChain::const_iterator::operator*() const |
| { |
| return *_b; |
| } |
| |
| inline IOBufferChain::const_iterator::value_type * |
| IOBufferChain::const_iterator::operator->() const |
| { |
| return _b; |
| } |
| |
| inline IOBufferChain::const_iterator & |
| IOBufferChain::const_iterator::operator++() |
| { |
| _b = _b->next.get(); |
| return *this; |
| } |
| |
| inline IOBufferChain::const_iterator |
| IOBufferChain::const_iterator::operator++(int) |
| { |
| self_type pre{*this}; |
| ++*this; |
| return pre; |
| } |
| |
| inline IOBufferChain::iterator::value_type & |
| IOBufferChain::iterator::operator*() const |
| { |
| return *_b; |
| } |
| |
| inline IOBufferChain::iterator::value_type * |
| IOBufferChain::iterator::operator->() const |
| { |
| return _b; |
| } |