| // 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. |
| |
| // iobuf - A non-continuous zero-copied buffer |
| |
| // Date: Thu Nov 22 13:57:56 CST 2012 |
| |
| #ifndef BUTIL_IOBUF_H |
| #define BUTIL_IOBUF_H |
| |
| #include <sys/uio.h> // iovec |
| #include <stdint.h> // uint32_t, int64_t |
| #include <functional> |
| #include <string> // std::string |
| #include <ostream> // std::ostream |
| #include <google/protobuf/io/zero_copy_stream.h> // ZeroCopyInputStream |
| #include "butil/strings/string_piece.h" // butil::StringPiece |
| #include "butil/third_party/snappy/snappy-sinksource.h" |
| #include "butil/zero_copy_stream_as_streambuf.h" |
| #include "butil/macros.h" |
| #include "butil/reader_writer.h" |
| #include "butil/binary_printer.h" |
| |
| // For IOBuf::appendv(const const_iovec*, size_t). The only difference of this |
| // struct from iovec (defined in sys/uio.h) is that iov_base is `const void*' |
| // which is assignable by const pointers w/o any error. |
| extern "C" { |
| struct const_iovec { |
| const void* iov_base; |
| size_t iov_len; |
| }; |
| #ifndef USE_MESALINK |
| struct ssl_st; |
| #else |
| #define ssl_st MESALINK_SSL |
| #endif |
| } |
| |
| namespace butil { |
| |
| // IOBuf is a non-continuous buffer that can be cut and combined w/o copying |
| // payload. It can be read from or flushed into file descriptors as well. |
| // IOBuf is [thread-compatible]. Namely using different IOBuf in different |
| // threads simultaneously is safe, and reading a static IOBuf from different |
| // threads is safe as well. |
| // IOBuf is [NOT thread-safe]. Modifying a same IOBuf from different threads |
| // simultaneously is unsafe and likely to crash. |
| class IOBuf { |
| friend class IOBufAsZeroCopyInputStream; |
| friend class IOBufAsZeroCopyOutputStream; |
| friend class IOBufBytesIterator; |
| friend class IOBufCutter; |
| friend class SingleIOBuf; |
| |
| public: |
| static const size_t DEFAULT_BLOCK_SIZE = 8192; |
| static const size_t INITIAL_CAP = 32; // must be power of 2 |
| |
| struct Block; |
| |
| // can't directly use `struct iovec' here because we also need to access the |
| // reference counter(nshared) in Block* |
| struct BlockRef { |
| // NOTICE: first bit of `offset' is shared with BigView::start |
| uint32_t offset; |
| uint32_t length; |
| Block* block; |
| }; |
| |
| // IOBuf is essentially a tiny queue of BlockRefs. |
| struct SmallView { |
| BlockRef refs[2]; |
| }; |
| |
| struct BigView { |
| int32_t magic; |
| uint32_t start; |
| BlockRef* refs; |
| uint32_t nref; |
| uint32_t cap_mask; |
| size_t nbytes; |
| |
| const BlockRef& ref_at(uint32_t i) const |
| { return refs[(start + i) & cap_mask]; } |
| |
| BlockRef& ref_at(uint32_t i) |
| { return refs[(start + i) & cap_mask]; } |
| |
| uint32_t capacity() const { return cap_mask + 1; } |
| }; |
| |
| struct Movable { |
| explicit Movable(IOBuf& v) : _v(&v) { } |
| IOBuf& value() const { return *_v; } |
| private: |
| IOBuf *_v; |
| }; |
| |
| typedef uint64_t Area; |
| static const Area INVALID_AREA = 0; |
| |
| IOBuf(); |
| IOBuf(const IOBuf&); |
| IOBuf(const Movable&); |
| ~IOBuf() { clear(); } |
| void operator=(const IOBuf&); |
| void operator=(const Movable&); |
| void operator=(const char*); |
| void operator=(const std::string&); |
| |
| // Exchange internal fields with another IOBuf. |
| void swap(IOBuf&); |
| |
| // Pop n bytes from front side |
| // If n == 0, nothing popped; if n >= length(), all bytes are popped |
| // Returns bytes popped. |
| size_t pop_front(size_t n); |
| |
| // Pop n bytes from back side |
| // If n == 0, nothing popped; if n >= length(), all bytes are popped |
| // Returns bytes popped. |
| size_t pop_back(size_t n); |
| |
| // Cut off n bytes from front side and APPEND to `out' |
| // If n == 0, nothing cut; if n >= length(), all bytes are cut |
| // Returns bytes cut. |
| size_t cutn(IOBuf* out, size_t n); |
| size_t cutn(void* out, size_t n); |
| size_t cutn(std::string* out, size_t n); |
| // Cut off 1 byte from the front side and set to *c |
| // Return true on cut, false otherwise. |
| bool cut1(void* c); |
| |
| // Cut from front side until the characters matches `delim', append |
| // data before the matched characters to `out'. |
| // Returns 0 on success, -1 when there's no match (including empty `delim') |
| // or other errors. |
| int cut_until(IOBuf* out, char const* delim); |
| |
| // std::string version, `delim' could be binary |
| int cut_until(IOBuf* out, const std::string& delim); |
| |
| // Cut at most `size_hint' bytes(approximately) into the writer |
| // Returns bytes cut on success, -1 otherwise and errno is set. |
| ssize_t cut_into_writer(IWriter* writer, size_t size_hint = 1024*1024); |
| |
| // Cut at most `size_hint' bytes(approximately) into the file descriptor |
| // Returns bytes cut on success, -1 otherwise and errno is set. |
| ssize_t cut_into_file_descriptor(int fd, size_t size_hint = 1024*1024); |
| |
| // Cut at most `size_hint' bytes(approximately) into the file descriptor at |
| // a given offset(from the start of the file). The file offset is not changed. |
| // If `offset' is negative, does exactly what cut_into_file_descriptor does. |
| // Returns bytes cut on success, -1 otherwise and errno is set. |
| // |
| // NOTE: POSIX requires that a file open with the O_APPEND flag should |
| // not affect pwrite(). However, on Linux, if |fd| is open with O_APPEND, |
| // pwrite() appends data to the end of the file, regardless of the value |
| // of |offset|. |
| ssize_t pcut_into_file_descriptor(int fd, off_t offset /*NOTE*/, |
| size_t size_hint = 1024*1024); |
| |
| // Cut into SSL channel `ssl'. Returns what `SSL_write' returns |
| // and the ssl error code will be filled into `ssl_error' |
| ssize_t cut_into_SSL_channel(struct ssl_st* ssl, int* ssl_error); |
| |
| // Cut `count' number of `pieces' into the writer. |
| // Returns bytes cut on success, -1 otherwise and errno is set. |
| static ssize_t cut_multiple_into_writer( |
| IWriter* writer, IOBuf* const* pieces, size_t count); |
| |
| // Cut `count' number of `pieces' into the file descriptor. |
| // Returns bytes cut on success, -1 otherwise and errno is set. |
| static ssize_t cut_multiple_into_file_descriptor( |
| int fd, IOBuf* const* pieces, size_t count); |
| |
| // Cut `count' number of `pieces' into file descriptor `fd' at a given |
| // offset. The file offset is not changed. |
| // If `offset' is negative, does exactly what cut_multiple_into_file_descriptor |
| // does. |
| // Read NOTE of pcut_into_file_descriptor. |
| // Returns bytes cut on success, -1 otherwise and errno is set. |
| static ssize_t pcut_multiple_into_file_descriptor( |
| int fd, off_t offset, IOBuf* const* pieces, size_t count); |
| |
| // Cut `count' number of `pieces' into SSL channel `ssl'. |
| // Returns bytes cut on success, -1 otherwise and errno is set. |
| static ssize_t cut_multiple_into_SSL_channel( |
| struct ssl_st* ssl, IOBuf* const* pieces, size_t count, int* ssl_error); |
| |
| // Append another IOBuf to back side, payload of the IOBuf is shared |
| // rather than copied. |
| void append(const IOBuf& other); |
| // Append content of `other' to self and clear `other'. |
| void append(const Movable& other); |
| |
| // =================================================================== |
| // Following push_back()/append() are just implemented for convenience |
| // and occasional usages, they're relatively slow because of the overhead |
| // of frequent BlockRef-management and reference-countings. If you get |
| // a lot of push_back/append to do, you should use IOBufAppender or |
| // IOBufBuilder instead, which reduce overhead by owning IOBuf::Block. |
| // =================================================================== |
| |
| // Append a character to back side. (with copying) |
| // Returns 0 on success, -1 otherwise. |
| int push_back(char c); |
| |
| // Append `data' with `count' bytes to back side. (with copying) |
| // Returns 0 on success(include count == 0), -1 otherwise. |
| int append(void const* data, size_t count); |
| |
| // Append multiple data to back side in one call, faster than appending |
| // one by one separately. |
| // Returns 0 on success, -1 otherwise. |
| // Example: |
| // const_iovec vec[] = { { data1, len1 }, |
| // { data2, len2 }, |
| // { data3, len3 } }; |
| // foo.appendv(vec, arraysize(vec)); |
| int appendv(const const_iovec vec[], size_t n); |
| int appendv(const iovec* vec, size_t n) |
| { return appendv((const const_iovec*)vec, n); } |
| |
| // Append a c-style string to back side. (with copying) |
| // Returns 0 on success, -1 otherwise. |
| // NOTE: Returns 0 when `s' is empty. |
| int append(char const* s); |
| |
| // Append a std::string to back side. (with copying) |
| // Returns 0 on success, -1 otherwise. |
| // NOTE: Returns 0 when `s' is empty. |
| int append(const std::string& s); |
| |
| // Append the user-data to back side WITHOUT copying. |
| // The user-data can be split and shared by smaller IOBufs and will be |
| // deleted using the deleter func when no IOBuf references it anymore. |
| int append_user_data(void* data, size_t size, std::function<void(void*)> deleter); |
| |
| // Append the user-data to back side WITHOUT copying. |
| // The meta is associated with this piece of user-data. |
| int append_user_data_with_meta(void* data, size_t size, std::function<void(void*)> deleter, uint64_t meta); |
| |
| // Get the data meta of the first byte in this IOBuf. |
| // The meta is specified with append_user_data_with_meta before. |
| // 0 means the meta is invalid. |
| uint64_t get_first_data_meta(); |
| |
| // Resizes the buf to a length of n characters. |
| // If n is smaller than the current length, all bytes after n will be |
| // truncated. |
| // If n is greater than the current length, the buffer would be append with |
| // as many |c| as needed to reach a size of n. If c is not specified, |
| // null-character would be appended. |
| // Returns 0 on success, -1 otherwise. |
| int resize(size_t n) { return resize(n, '\0'); } |
| int resize(size_t n, char c); |
| |
| // Reserve `n' uninitialized bytes at back-side. |
| // Returns an object representing the reserved area, INVALID_AREA on failure. |
| // NOTE: reserve(0) returns INVALID_AREA. |
| Area reserve(size_t n); |
| |
| // [EXTREMELY UNSAFE] |
| // Copy `data' to the reserved `area'. `data' must be as long as the |
| // reserved size. |
| // Returns 0 on success, -1 otherwise. |
| // [Rules] |
| // 1. Make sure the IOBuf to be assigned was NOT cut/pop from front side |
| // after reserving, otherwise behavior of this function is undefined, |
| // even if it returns 0. |
| // 2. Make sure the IOBuf to be assigned was NOT copied to/from another |
| // IOBuf after reserving to prevent underlying blocks from being shared, |
| // otherwise the assignment affects all IOBuf sharing the blocks, which |
| // is probably not what we want. |
| int unsafe_assign(Area area, const void* data); |
| |
| // Append min(n, length()) bytes starting from `pos' at front side to `buf'. |
| // The real payload is shared rather than copied. |
| // Returns bytes copied. |
| size_t append_to(IOBuf* buf, size_t n = (size_t)-1L, size_t pos = 0) const; |
| |
| // Explicitly declare this overload as error to avoid copy_to(butil::IOBuf*) |
| // from being interpreted as copy_to(void*) by the compiler (which causes |
| // undefined behavior). |
| size_t copy_to(IOBuf* buf, size_t n = (size_t)-1L, size_t pos = 0) const |
| // the error attribute in not available in gcc 3.4 |
| #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) |
| __attribute__ (( error("Call append_to(IOBuf*) instead") )) |
| #endif |
| ; |
| |
| // Copy min(n, length()) bytes starting from `pos' at front side into `buf'. |
| // Returns bytes copied. |
| size_t copy_to(void* buf, size_t n = (size_t)-1L, size_t pos = 0) const; |
| |
| // NOTE: first parameter is not std::string& because user may pass in |
| // a pointer of std::string by mistake, in which case, the void* overload |
| // would be wrongly called. |
| size_t copy_to(std::string* s, size_t n = (size_t)-1L, size_t pos = 0) const; |
| size_t append_to(std::string* s, size_t n = (size_t)-1L, size_t pos = 0) const; |
| |
| // Copy min(n, length()) bytes staring from `pos' at front side into |
| // `cstr' and end it with '\0'. |
| // `cstr' must be as long as min(n, length())+1. |
| // Returns bytes copied (not including ending '\0') |
| size_t copy_to_cstr(char* cstr, size_t n = (size_t)-1L, size_t pos = 0) const; |
| |
| // Convert all data in this buffer to a std::string. |
| std::string to_string() const; |
| |
| // Get `n' front-side bytes with minimum copying. Length of `aux_buffer' |
| // must not be less than `n'. |
| // Returns: |
| // NULL - n is greater than length() |
| // aux_buffer - n bytes are copied into aux_buffer |
| // internal buffer - the bytes are stored continuously in the internal |
| // buffer, no copying is needed. This function does not |
| // add additional reference to the underlying block, |
| // so user should not change this IOBuf during using |
| // the internal buffer. |
| // If n == 0 and buffer is empty, return value is undefined. |
| const void* fetch(void* aux_buffer, size_t n) const; |
| // Fetch one character from front side. |
| // Returns pointer to the character, NULL on empty. |
| const void* fetch1() const; |
| |
| // Remove all data |
| void clear(); |
| |
| // True iff there's no data |
| bool empty() const; |
| |
| // Number of bytes |
| size_t length() const; |
| size_t size() const { return length(); } |
| |
| // Get number of Blocks in use. block_memory = block_count * BLOCK_SIZE |
| static size_t block_count(); |
| static size_t block_memory(); |
| static size_t new_bigview_count(); |
| static size_t block_count_hit_tls_threshold(); |
| |
| // Equal with a string/IOBuf or not. |
| bool equals(const butil::StringPiece&) const; |
| bool equals(const IOBuf& other) const; |
| |
| // Get the number of backing blocks |
| size_t backing_block_num() const { return _ref_num(); } |
| |
| // Get #i backing_block, an empty StringPiece is returned if no such block |
| StringPiece backing_block(size_t i) const; |
| |
| // Make a movable version of self |
| Movable movable() { return Movable(*this); } |
| |
| protected: |
| int _cut_by_char(IOBuf* out, char); |
| int _cut_by_delim(IOBuf* out, char const* dbegin, size_t ndelim); |
| |
| // Returns: true iff this should be viewed as SmallView |
| bool _small() const; |
| |
| template <bool MOVE> |
| void _push_or_move_back_ref_to_smallview(const BlockRef&); |
| template <bool MOVE> |
| void _push_or_move_back_ref_to_bigview(const BlockRef&); |
| |
| // Push a BlockRef to back side |
| // NOTICE: All fields of the ref must be initialized or assigned |
| // properly, or it will ruin this queue |
| void _push_back_ref(const BlockRef&); |
| // Move a BlockRef to back side. After calling this function, content of |
| // the BlockRef will be invalid and should never be used again. |
| void _move_back_ref(const BlockRef&); |
| |
| // Pop a BlockRef from front side. |
| // Returns: 0 on success and -1 on empty. |
| int _pop_front_ref() { return _pop_or_moveout_front_ref<false>(); } |
| |
| // Move a BlockRef out from front side. |
| // Returns: 0 on success and -1 on empty. |
| int _moveout_front_ref() { return _pop_or_moveout_front_ref<true>(); } |
| |
| template <bool MOVEOUT> |
| int _pop_or_moveout_front_ref(); |
| |
| // Pop a BlockRef from back side. |
| // Returns: 0 on success and -1 on empty. |
| int _pop_back_ref(); |
| |
| // Number of refs in the queue |
| size_t _ref_num() const; |
| |
| // Get reference to front/back BlockRef in the queue |
| // should not be called if queue is empty or the behavior is undefined |
| BlockRef& _front_ref(); |
| const BlockRef& _front_ref() const; |
| BlockRef& _back_ref(); |
| const BlockRef& _back_ref() const; |
| |
| // Get reference to n-th BlockRef(counting from front) in the queue |
| // NOTICE: should not be called if queue is empty and the `n' must |
| // be inside [0, _ref_num()-1] or behavior is undefined |
| BlockRef& _ref_at(size_t i); |
| const BlockRef& _ref_at(size_t i) const; |
| |
| // Get pointer to n-th BlockRef(counting from front) |
| // If i is out-of-range, NULL is returned. |
| const BlockRef* _pref_at(size_t i) const; |
| |
| private: |
| union { |
| BigView _bv; |
| SmallView _sv; |
| }; |
| }; |
| |
| std::ostream& operator<<(std::ostream&, const IOBuf& buf); |
| |
| inline bool operator==(const butil::IOBuf& b, const butil::StringPiece& s) |
| { return b.equals(s); } |
| inline bool operator==(const butil::StringPiece& s, const butil::IOBuf& b) |
| { return b.equals(s); } |
| inline bool operator!=(const butil::IOBuf& b, const butil::StringPiece& s) |
| { return !b.equals(s); } |
| inline bool operator!=(const butil::StringPiece& s, const butil::IOBuf& b) |
| { return !b.equals(s); } |
| inline bool operator==(const butil::IOBuf& b1, const butil::IOBuf& b2) |
| { return b1.equals(b2); } |
| inline bool operator!=(const butil::IOBuf& b1, const butil::IOBuf& b2) |
| { return !b1.equals(b2); } |
| |
| // IOPortal is a subclass of IOBuf that can read from file descriptors. |
| // Typically used as the buffer to store bytes from sockets. |
| class IOPortal : public IOBuf { |
| public: |
| IOPortal() : _block(NULL) { } |
| IOPortal(const IOPortal& rhs) : IOBuf(rhs), _block(NULL) { } |
| ~IOPortal(); |
| IOPortal& operator=(const IOPortal& rhs); |
| |
| // Read at most `max_count' bytes from the reader and append to self. |
| ssize_t append_from_reader(IReader* reader, size_t max_count); |
| |
| // Read at most `max_count' bytes from file descriptor `fd' and |
| // append to self. |
| ssize_t append_from_file_descriptor(int fd, size_t max_count); |
| |
| // Read at most `max_count' bytes from file descriptor `fd' at a given |
| // offset and append to self. The file offset is not changed. |
| // If `offset' is negative, does exactly what append_from_file_descriptor does. |
| ssize_t pappend_from_file_descriptor(int fd, off_t offset, size_t max_count); |
| |
| // Read as many bytes as possible from SSL channel `ssl', and stop until `max_count'. |
| // Returns total bytes read and the ssl error code will be filled into `ssl_error' |
| ssize_t append_from_SSL_channel(struct ssl_st* ssl, int* ssl_error, |
| size_t max_count = 1024*1024); |
| |
| // Remove all data inside and return cached blocks. |
| void clear(); |
| |
| // Return cached blocks to TLS. This function should be called by users |
| // when this IOPortal are cut into intact messages and becomes empty, to |
| // let continuing code on IOBuf to reuse the blocks. Calling this function |
| // after each call to append_xxx does not make sense and may hurt |
| // performance. Read comments on field `_block' below. |
| void return_cached_blocks(); |
| |
| private: |
| static void return_cached_blocks_impl(Block*); |
| |
| // Cached blocks for appending. Notice that the blocks are released |
| // until return_cached_blocks()/clear()/dtor() are called, rather than |
| // released after each append_xxx(), which makes messages read from one |
| // file descriptor more likely to share blocks and have less BlockRefs. |
| Block* _block; |
| }; |
| |
| class IOReserveAlignedBuf : public IOBuf { |
| public: |
| IOReserveAlignedBuf(size_t alignment) |
| : _alignment(alignment), _reserved(false) {} |
| Area reserve(size_t count); |
| |
| private: |
| size_t _alignment; |
| bool _reserved; |
| }; |
| |
| // Specialized utility to cut from IOBuf faster than using corresponding |
| // methods in IOBuf. |
| // Designed for efficiently parsing data from IOBuf. |
| // The cut IOBuf can be appended during cutting. |
| class IOBufCutter { |
| public: |
| explicit IOBufCutter(butil::IOBuf* buf); |
| ~IOBufCutter(); |
| |
| // Cut off n bytes and APPEND to `out' |
| // Returns bytes cut. |
| size_t cutn(butil::IOBuf* out, size_t n); |
| size_t cutn(std::string* out, size_t n); |
| size_t cutn(void* out, size_t n); |
| |
| // Cut off 1 byte from the front side and set to *c |
| // Return true on cut, false otherwise. |
| bool cut1(void* data); |
| |
| // Copy n bytes into `data' |
| // Returns bytes copied. |
| size_t copy_to(void* data, size_t n); |
| |
| // Fetch one character. |
| // Returns pointer to the character, NULL on empty |
| const void* fetch1(); |
| |
| // Pop n bytes from front side |
| // Returns bytes popped. |
| size_t pop_front(size_t n); |
| |
| // Uncut bytes |
| size_t remaining_bytes() const; |
| |
| private: |
| size_t slower_copy_to(void* data, size_t n); |
| bool load_next_ref(); |
| |
| private: |
| void* _data; |
| void* _data_end; |
| IOBuf::Block* _block; |
| IOBuf* _buf; |
| }; |
| |
| // Parse protobuf message from IOBuf. Notice that this wrapper does not change |
| // source IOBuf, which also should not change during lifetime of the wrapper. |
| // Even if a IOBufAsZeroCopyInputStream is created but parsed, the source |
| // IOBuf should not be changed as well becuase constructor of the stream |
| // saves internal information of the source IOBuf which is assumed to be |
| // unchanged. |
| // Example: |
| // IOBufAsZeroCopyInputStream wrapper(the_iobuf_with_protobuf_format_data); |
| // some_pb_message.ParseFromZeroCopyStream(&wrapper); |
| class IOBufAsZeroCopyInputStream |
| : public google::protobuf::io::ZeroCopyInputStream { |
| public: |
| explicit IOBufAsZeroCopyInputStream(const IOBuf&); |
| |
| bool Next(const void** data, int* size) override; |
| void BackUp(int count) override; |
| bool Skip(int count) override; |
| int64_t ByteCount() const override; |
| |
| private: |
| int _ref_index; |
| int _add_offset; |
| int64_t _byte_count; |
| const IOBuf* _buf; |
| }; |
| |
| // Serialize protobuf message into IOBuf. This wrapper does not clear source |
| // IOBuf before appending. You can change the source IOBuf when stream is |
| // not used(append sth. to the IOBuf, serialize a protobuf message, append |
| // sth. again, serialize messages again...). This is different from |
| // IOBufAsZeroCopyInputStream which needs the source IOBuf to be unchanged. |
| // Example: |
| // IOBufAsZeroCopyOutputStream wrapper(&the_iobuf_to_put_data_in); |
| // some_pb_message.SerializeToZeroCopyStream(&wrapper); |
| // |
| // NOTE: Blocks are by default shared among all the ZeroCopyOutputStream in one |
| // thread. If there are many manipulated streams at one time, there may be many |
| // fragments. You can create a ZeroCopyOutputStream which has its own block by |
| // passing a positive `block_size' argument to avoid this problem. |
| class IOBufAsZeroCopyOutputStream |
| : public google::protobuf::io::ZeroCopyOutputStream { |
| public: |
| explicit IOBufAsZeroCopyOutputStream(IOBuf*); |
| IOBufAsZeroCopyOutputStream(IOBuf*, uint32_t block_size); |
| ~IOBufAsZeroCopyOutputStream(); |
| |
| bool Next(void** data, int* size) override; |
| void BackUp(int count) override; // `count' can be as long as ByteCount() |
| int64_t ByteCount() const override; |
| |
| private: |
| void _release_block(); |
| |
| IOBuf* _buf; |
| uint32_t _block_size; |
| IOBuf::Block *_cur_block; |
| int64_t _byte_count; |
| }; |
| |
| // Wrap IOBuf into input of snappy compression. |
| class IOBufAsSnappySource : public butil::snappy::Source { |
| public: |
| explicit IOBufAsSnappySource(const butil::IOBuf& buf) |
| : _buf(&buf), _stream(buf) {} |
| virtual ~IOBufAsSnappySource() {} |
| |
| // Return the number of bytes left to read from the source |
| size_t Available() const override; |
| |
| // Peek at the next flat region of the source. |
| const char* Peek(size_t* len) override; |
| |
| // Skip the next n bytes. Invalidates any buffer returned by |
| // a previous call to Peek(). |
| void Skip(size_t n) override; |
| |
| private: |
| const butil::IOBuf* _buf; |
| butil::IOBufAsZeroCopyInputStream _stream; |
| }; |
| |
| // Wrap IOBuf into output of snappy compression. |
| class IOBufAsSnappySink : public butil::snappy::Sink { |
| public: |
| explicit IOBufAsSnappySink(butil::IOBuf& buf); |
| virtual ~IOBufAsSnappySink() {} |
| |
| // Append "bytes[0,n-1]" to this. |
| void Append(const char* bytes, size_t n) override; |
| |
| // Returns a writable buffer of the specified length for appending. |
| char* GetAppendBuffer(size_t length, char* scratch) override; |
| |
| private: |
| char* _cur_buf; |
| int _cur_len; |
| butil::IOBuf* _buf; |
| butil::IOBufAsZeroCopyOutputStream _buf_stream; |
| }; |
| |
| // A std::ostream to build IOBuf. |
| // Example: |
| // IOBufBuilder builder; |
| // builder << "Anything that can be sent to std::ostream"; |
| // // You have several methods to fetch the IOBuf. |
| // target_iobuf.append(builder.buf()); // builder.buf() was not changed |
| // OR |
| // builder.move_to(target_iobuf); // builder.buf() was clear()-ed. |
| class IOBufBuilder : |
| // Have to use private inheritance to arrange initialization order. |
| virtual private IOBuf, |
| virtual private IOBufAsZeroCopyOutputStream, |
| virtual private ZeroCopyStreamAsStreamBuf, |
| public std::ostream { |
| public: |
| explicit IOBufBuilder() |
| : IOBufAsZeroCopyOutputStream(this) |
| , ZeroCopyStreamAsStreamBuf(this) |
| , std::ostream(this) |
| { } |
| |
| IOBuf& buf() { |
| this->shrink(); |
| return *this; |
| } |
| void buf(const IOBuf& buf) { |
| *static_cast<IOBuf*>(this) = buf; |
| } |
| void move_to(IOBuf& target) { |
| target = Movable(buf()); |
| } |
| }; |
| |
| // Create IOBuf by appending data *faster* |
| class IOBufAppender { |
| public: |
| IOBufAppender(); |
| |
| // Append `n' bytes starting from `data' to back side of the internal buffer |
| // Costs 2/3 time of IOBuf.append for short data/strings on Intel(R) Xeon(R) |
| // CPU E5-2620 @ 2.00GHz. Longer data/strings make differences smaller. |
| // Returns 0 on success, -1 otherwise. |
| int append(const void* data, size_t n); |
| int append(const butil::StringPiece& str); |
| |
| // Format integer |d| to back side of the internal buffer, which is much faster |
| // than snprintf(..., "%lu", d). |
| // Returns 0 on success, -1 otherwise. |
| int append_decimal(long d); |
| |
| // Push the character to back side of the internal buffer. |
| // Costs ~3ns while IOBuf.push_back costs ~13ns on Intel(R) Xeon(R) CPU |
| // E5-2620 @ 2.00GHz |
| // Returns 0 on success, -1 otherwise. |
| int push_back(char c); |
| |
| IOBuf& buf() { |
| shrink(); |
| return _buf; |
| } |
| void move_to(IOBuf& target) { |
| target = IOBuf::Movable(buf()); |
| } |
| |
| private: |
| void shrink(); |
| int add_block(); |
| |
| void* _data; |
| // Saving _data_end instead of _size avoid modifying _data and _size |
| // in each push_back() which is probably a hotspot. |
| void* _data_end; |
| IOBuf _buf; |
| IOBufAsZeroCopyOutputStream _zc_stream; |
| }; |
| |
| // Iterate bytes of a IOBuf. |
| // During iteration, the iobuf should NOT be changed. |
| class IOBufBytesIterator { |
| public: |
| explicit IOBufBytesIterator(const butil::IOBuf& buf); |
| // Construct from another iterator. |
| IOBufBytesIterator(const IOBufBytesIterator& it); |
| IOBufBytesIterator(const IOBufBytesIterator& it, size_t bytes_left); |
| // Returning unsigned is safer than char which would be more error prone |
| // to bitwise operations. For example: in "uint32_t value = *it", value |
| // is (unexpected) 4294967168 when *it returns (char)128. |
| unsigned char operator*() const { return (unsigned char)*_block_begin; } |
| operator const void*() const { return (const void*)!!_bytes_left; } |
| void operator++(); |
| void operator++(int) { return operator++(); } |
| // Copy at most n bytes into buf, forwarding this iterator. |
| // Returns bytes copied. |
| size_t copy_and_forward(void* buf, size_t n); |
| size_t copy_and_forward(std::string* s, size_t n); |
| // Just forward this iterator for at most n bytes. |
| size_t forward(size_t n); |
| // Append at most n bytes into buf, forwarding this iterator. Data are |
| // referenced rather than copied. |
| size_t append_and_forward(butil::IOBuf* buf, size_t n); |
| bool forward_one_block(const void** data, size_t* size); |
| size_t bytes_left() const { return _bytes_left; } |
| private: |
| void try_next_block(); |
| const char* _block_begin; |
| const char* _block_end; |
| uint32_t _block_count; |
| uint32_t _bytes_left; |
| const butil::IOBuf* _buf; |
| }; |
| |
| } // namespace butil |
| |
| // Specialize std::swap for IOBuf |
| #if __cplusplus < 201103L // < C++11 |
| #include <algorithm> // std::swap until C++11 |
| #else |
| #include <utility> // std::swap since C++11 |
| #endif // __cplusplus < 201103L |
| namespace std { |
| template <> |
| inline void swap(butil::IOBuf& a, butil::IOBuf& b) { |
| return a.swap(b); |
| } |
| } // namespace std |
| |
| #include "butil/iobuf_inl.h" |
| |
| #endif // BUTIL_IOBUF_H |