| 1 | #pragma once |
| 2 | |
| 3 | #include <Core/Defines.h> |
| 4 | #include <algorithm> |
| 5 | |
| 6 | |
| 7 | namespace DB |
| 8 | { |
| 9 | |
| 10 | |
| 11 | /** Base class for ReadBuffer and WriteBuffer. |
| 12 | * Contains common types, variables, and functions. |
| 13 | * |
| 14 | * ReadBuffer and WriteBuffer are similar to istream and ostream, respectively. |
| 15 | * They have to be used, because using iostreams it is impossible to effectively implement some operations. |
| 16 | * For example, using istream, you can not quickly read string values from a tab-separated file, |
| 17 | * so that after reading, the position remains immediately after the read value. |
| 18 | * (The only option is to call the std::istream::get() function on each byte, but this slows down due to several virtual calls.) |
| 19 | * |
| 20 | * Read/WriteBuffers provide direct access to the internal buffer, so the necessary operations are implemented more efficiently. |
| 21 | * Only one virtual function nextImpl() is used, which is rarely called: |
| 22 | * - in the case of ReadBuffer - fill in the buffer with new data from the source; |
| 23 | * - in the case of WriteBuffer - write data from the buffer into the receiver. |
| 24 | * |
| 25 | * Read/WriteBuffer can own or not own an own piece of memory. |
| 26 | * In the second case, you can effectively read from an already existing piece of memory / std::string without copying it. |
| 27 | */ |
| 28 | class BufferBase |
| 29 | { |
| 30 | public: |
| 31 | /** Cursor in the buffer. The position of write or read. */ |
| 32 | using Position = char *; |
| 33 | |
| 34 | /** A reference to the range of memory. */ |
| 35 | struct Buffer |
| 36 | { |
| 37 | Buffer(Position begin_pos_, Position end_pos_) : begin_pos(begin_pos_), end_pos(end_pos_) {} |
| 38 | |
| 39 | inline Position begin() const { return begin_pos; } |
| 40 | inline Position end() const { return end_pos; } |
| 41 | inline size_t size() const { return size_t(end_pos - begin_pos); } |
| 42 | inline void resize(size_t size) { end_pos = begin_pos + size; } |
| 43 | |
| 44 | inline void swap(Buffer & other) |
| 45 | { |
| 46 | std::swap(begin_pos, other.begin_pos); |
| 47 | std::swap(end_pos, other.end_pos); |
| 48 | } |
| 49 | |
| 50 | private: |
| 51 | Position begin_pos; |
| 52 | Position end_pos; /// 1 byte after the end of the buffer |
| 53 | }; |
| 54 | |
| 55 | /** The constructor takes a range of memory to use for the buffer. |
| 56 | * offset - the starting point of the cursor. ReadBuffer must set it to the end of the range, and WriteBuffer - to the beginning. |
| 57 | */ |
| 58 | BufferBase(Position ptr, size_t size, size_t offset) |
| 59 | : pos(ptr + offset), working_buffer(ptr, ptr + size), internal_buffer(ptr, ptr + size) {} |
| 60 | |
| 61 | void set(Position ptr, size_t size, size_t offset) |
| 62 | { |
| 63 | internal_buffer = Buffer(ptr, ptr + size); |
| 64 | working_buffer = Buffer(ptr, ptr + size); |
| 65 | pos = ptr + offset; |
| 66 | } |
| 67 | |
| 68 | /// get buffer |
| 69 | inline Buffer & internalBuffer() { return internal_buffer; } |
| 70 | |
| 71 | /// get the part of the buffer from which you can read / write data |
| 72 | inline Buffer & buffer() { return working_buffer; } |
| 73 | |
| 74 | /// get (for reading and modifying) the position in the buffer |
| 75 | inline Position & position() { return pos; } |
| 76 | |
| 77 | /// offset in bytes of the cursor from the beginning of the buffer |
| 78 | inline size_t offset() const { return size_t(pos - working_buffer.begin()); } |
| 79 | |
| 80 | /// How many bytes are available for read/write |
| 81 | inline size_t available() const { return size_t(working_buffer.end() - pos); } |
| 82 | |
| 83 | inline void swap(BufferBase & other) |
| 84 | { |
| 85 | internal_buffer.swap(other.internal_buffer); |
| 86 | working_buffer.swap(other.working_buffer); |
| 87 | std::swap(pos, other.pos); |
| 88 | } |
| 89 | |
| 90 | /** How many bytes have been read/written, counting those that are still in the buffer. */ |
| 91 | size_t count() const { return bytes + offset(); } |
| 92 | |
| 93 | /** Check that there is more bytes in buffer after cursor. */ |
| 94 | bool ALWAYS_INLINE hasPendingData() const { return available() > 0; } |
| 95 | |
| 96 | bool isPadded() const { return padded; } |
| 97 | |
| 98 | protected: |
| 99 | /// Read/write position. |
| 100 | Position pos; |
| 101 | |
| 102 | /** How many bytes have been read/written, not counting those that are now in the buffer. |
| 103 | * (counting those that were already used and "removed" from the buffer) |
| 104 | */ |
| 105 | size_t bytes = 0; |
| 106 | |
| 107 | /** A piece of memory that you can use. |
| 108 | * For example, if internal_buffer is 1MB, and from a file for reading it was loaded into the buffer |
| 109 | * only 10 bytes, then working_buffer will be 10 bytes in size |
| 110 | * (working_buffer.end() will point to the position immediately after the 10 bytes that can be read). |
| 111 | */ |
| 112 | Buffer working_buffer; |
| 113 | |
| 114 | /// A reference to a piece of memory for the buffer. |
| 115 | Buffer internal_buffer; |
| 116 | |
| 117 | /// Indicator of 15 bytes pad_right |
| 118 | bool padded{false}; |
| 119 | }; |
| 120 | |
| 121 | |
| 122 | } |
| 123 | |