/*
- * Copyright 2015 Facebook, Inc.
+ * Copyright 2017 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* limitations under the License.
*/
-#ifndef FOLLY_IO_IOBUF_H_
-#define FOLLY_IO_IOBUF_H_
+#pragma once
#include <glog/logging.h>
#include <atomic>
#include <cstring>
#include <memory>
#include <limits>
-#include <sys/uio.h>
#include <type_traits>
#include <boost/iterator/iterator_facade.hpp>
#include <folly/FBString.h>
#include <folly/Range.h>
#include <folly/FBVector.h>
+#include <folly/portability/SysUio.h>
// Ignore shadowing warnings within this file, so includers can use -Wshadow.
#pragma GCC diagnostic push
* an IOBuf chain must be heap allocated. (All functions to add nodes to a
* chain require a std::unique_ptr<IOBuf>, which enforces this requrement.)
*
- * Additionally, no copy-constructor or assignment operator currently exists,
- * so stack-allocated IOBufs may only be moved, not copied. (Technically
- * nothing is preventing us from adding a copy constructor and assignment
- * operator. However, it seems like this would add the possibility for some
- * confusion. We would need to determine if these functions would copy just a
- * single buffer, or the entire chain.)
- *
+ * Copying IOBufs is only meaningful for the head of a chain. The entire chain
+ * is cloned; the IOBufs will become shared, and the old and new IOBufs will
+ * refer to the same underlying memory.
*
* IOBuf Sharing
* -------------
static std::unique_ptr<IOBuf> wrapBuffer(ByteRange br) {
return wrapBuffer(br.data(), br.size());
}
+
+ /**
+ * Similar to wrapBuffer(), but returns IOBuf by value rather than
+ * heap-allocating it.
+ */
+ static IOBuf wrapBufferAsValue(const void* buf, uint64_t capacity);
+ static IOBuf wrapBufferAsValue(ByteRange br) {
+ return wrapBufferAsValue(br.data(), br.size());
+ }
+
IOBuf(WrapBufferOp op, const void* buf, uint64_t capacity);
IOBuf(WrapBufferOp op, ByteRange br);
* Returns the number of bytes in the buffer before the start of the data.
*/
uint64_t headroom() const {
- return data_ - buffer();
+ return uint64_t(data_ - buffer());
}
/**
* Returns the number of bytes in the buffer after the end of the data.
*/
uint64_t tailroom() const {
- return bufferEnd() - tail();
+ return uint64_t(bufferEnd() - tail());
}
/**
}
}
+ /**
+ * Return true if all IOBufs in this chain are managed by the usual
+ * refcounting mechanism (and so the lifetime of the underlying memory
+ * can be extended by clone()).
+ */
+ bool isManaged() const {
+ const IOBuf* current = this;
+ while (true) {
+ if (!current->isManagedOne()) {
+ return false;
+ }
+ current = current->next_;
+ if (current == this) {
+ return true;
+ }
+ }
+ }
+
+ /**
+ * Return true if this IOBuf is managed by the usual refcounting mechanism
+ * (and so the lifetime of the underlying memory can be extended by
+ * cloneOne()).
+ */
+ bool isManagedOne() const {
+ return sharedInfo();
+ }
+
/**
* Return true if other IOBufs are also pointing to the buffer used by this
* IOBuf, and false otherwise.
return true;
}
+ if (UNLIKELY(sharedInfo()->externallyShared)) {
+ return true;
+ }
+
if (LIKELY(!(flags() & kFlagMaybeShared))) {
return false;
}
}
}
+ /**
+ * Mark the underlying buffers in this chain as shared with external memory
+ * management mechanism. This will make isShared() always returns true.
+ *
+ * This function is not thread-safe, and only safe to call immediately after
+ * creating an IOBuf, before it has been shared with other threads.
+ */
+ void markExternallyShared();
+
+ /**
+ * Mark the underlying buffer that this IOBuf refers to as shared with
+ * external memory management mechanism. This will make isSharedOne() always
+ * returns true.
+ *
+ * This function is not thread-safe, and only safe to call immediately after
+ * creating an IOBuf, before it has been shared with other threads.
+ */
+ void markExternallySharedOne() {
+ SharedInfo* info = sharedInfo();
+ if (info) {
+ info->externallyShared = true;
+ }
+ }
+
+ /**
+ * Ensure that the memory that IOBufs in this chain refer to will continue to
+ * be allocated for as long as the IOBufs of the chain (or any clone()s
+ * created from this point onwards) is alive.
+ *
+ * This only has an effect for user-owned buffers (created with the
+ * WRAP_BUFFER constructor or wrapBuffer factory function), in which case
+ * those buffers are unshared.
+ */
+ void makeManaged() {
+ if (isChained()) {
+ makeManagedChained();
+ } else {
+ makeManagedOne();
+ }
+ }
+
+ /**
+ * Ensure that the memory that this IOBuf refers to will continue to be
+ * allocated for as long as this IOBuf (or any clone()s created from this
+ * point onwards) is alive.
+ *
+ * This only has an effect for user-owned buffers (created with the
+ * WRAP_BUFFER constructor or wrapBuffer factory function), in which case
+ * those buffers are unshared.
+ */
+ void makeManagedOne() {
+ if (!isManagedOne()) {
+ // We can call the internal function directly; unmanaged implies shared.
+ unshareOneSlow();
+ }
+ }
+
/**
* Coalesce this IOBuf chain into a single buffer.
*
*/
std::unique_ptr<IOBuf> clone() const;
+ /**
+ * Similar to clone(). But returns IOBuf by value rather than heap-allocating
+ * it.
+ */
+ IOBuf cloneAsValue() const;
+
/**
* Return a new IOBuf with the same data as this IOBuf.
*
*/
std::unique_ptr<IOBuf> cloneOne() const;
+ /**
+ * Similar to cloneOne(). But returns IOBuf by value rather than
+ * heap-allocating it.
+ */
+ IOBuf cloneOneAsValue() const;
+
/**
* Similar to Clone(). But use other as the head node. Other nodes in the
* chain (if any) will be allocted on heap.
*/
- void cloneInto(IOBuf& other) const;
+ void cloneInto(IOBuf& other) const {
+ other = cloneAsValue();
+ }
/**
* Similar to CloneOne(). But to fill an existing IOBuf instead of a new
* IOBuf.
*/
- void cloneOneInto(IOBuf& other) const;
+ void cloneOneInto(IOBuf& other) const {
+ other = cloneOneAsValue();
+ }
/**
* Return an iovector suitable for e.g. writev()
* the head of an IOBuf chain or a solitary IOBuf not part of a chain. If
* the move destination is part of a chain, all other IOBufs in the chain
* will be deleted.
- *
- * (We currently don't provide a copy constructor or assignment operator.
- * The main reason is because it is not clear these operations should copy
- * the entire chain or just the single IOBuf.)
*/
IOBuf(IOBuf&& other) noexcept;
IOBuf& operator=(IOBuf&& other) noexcept;
+ IOBuf(const IOBuf& other);
+ IOBuf& operator=(const IOBuf& other);
+
private:
enum FlagsEnum : uintptr_t {
// Adding any more flags would not work on 32-bit architectures,
FreeFunction freeFn;
void* userData;
std::atomic<uint32_t> refcount;
+ bool externallyShared{false};
};
// Helper structs for use by operator new and delete
struct HeapPrefix;
struct HeapStorage;
struct HeapFullStorage;
- // Forbidden copy constructor and assignment opererator
- IOBuf(IOBuf const &);
- IOBuf& operator=(IOBuf const &);
-
/**
* Create a new IOBuf pointing to an external buffer.
*
void unshareOneSlow();
void unshareChained();
+ void makeManagedChained();
void coalesceSlow();
void coalesceSlow(size_t maxLength);
// newLength must be the entire length of the buffers between this and
static inline uintptr_t packFlagsAndSharedInfo(uintptr_t flags,
SharedInfo* info) {
uintptr_t uinfo = reinterpret_cast<uintptr_t>(info);
- DCHECK_EQ(flags & ~kFlagMask, 0);
- DCHECK_EQ(uinfo & kFlagMask, 0);
+ DCHECK_EQ(flags & ~kFlagMask, 0u);
+ DCHECK_EQ(uinfo & kFlagMask, 0u);
return flags | uinfo;
}
inline void setSharedInfo(SharedInfo* info) {
uintptr_t uinfo = reinterpret_cast<uintptr_t>(info);
- DCHECK_EQ(uinfo & kFlagMask, 0);
+ DCHECK_EQ(uinfo & kFlagMask, 0u);
flagsAndSharedInfo_ = (flagsAndSharedInfo_ & kFlagMask) | uinfo;
}
// flags_ are changed from const methods
inline void setFlags(uintptr_t flags) const {
- DCHECK_EQ(flags & ~kFlagMask, 0);
+ DCHECK_EQ(flags & ~kFlagMask, 0u);
flagsAndSharedInfo_ |= flags;
}
inline void clearFlags(uintptr_t flags) const {
- DCHECK_EQ(flags & ~kFlagMask, 0);
+ DCHECK_EQ(flags & ~kFlagMask, 0u);
flagsAndSharedInfo_ &= ~flags;
}
uint64_t capacity = headroom + size + minTailroom;
std::unique_ptr<IOBuf> buf = create(capacity);
buf->advance(headroom);
- memcpy(buf->writableData(), data, size);
+ if (size != 0) {
+ memcpy(buf->writableData(), data, size);
+ }
buf->append(size);
return buf;
}
}
}
+ Iterator() {}
+
private:
void setVal() {
val_ = ByteRange(pos_->data(), pos_->tail());
adjustForEnd();
}
- const IOBuf* pos_;
- const IOBuf* end_;
+ const IOBuf* pos_{nullptr};
+ const IOBuf* end_{nullptr};
ByteRange val_;
};
} // folly
#pragma GCC diagnostic pop
-
-#endif // FOLLY_IO_IOBUF_H_