: raw(dataptr, l, mempool) {
}
- static ceph::unique_leakable_ptr<buffer::raw>
- create(unsigned len,
- unsigned align,
- int mempool = mempool::mempool_buffer_anon)
+ static std::pair<char*, size_t> alloc_data_n_controlblock(
+ unsigned len,
+ unsigned align)
{
// posix_memalign() requires a multiple of sizeof(void *)
align = std::max<unsigned>(align, sizeof(void *));
#endif /* DARWIN */
if (!ptr)
throw bad_alloc();
+ return {ptr, datalen};
+ }
+ static ceph::unique_leakable_ptr<buffer::raw>
+ create(unsigned len,
+ unsigned align,
+ int mempool = mempool::mempool_buffer_anon)
+ {
+ const auto [ptr, datalen] = alloc_data_n_controlblock(len, align);
// actual data first, since it has presumably larger alignment restriction
// then put the raw_combined at the end
return ceph::unique_leakable_ptr<buffer::raw>(
}
};
+ class buffer::raw_zeros : public buffer::raw_combined {
+ raw_zeros(char *dataptr, unsigned l, int mempool)
+ : raw_combined(dataptr, l, mempool) {
+ memset(dataptr, 0, l);
+ }
+
+ static constexpr unsigned ZERO_AREA_NUM_PAGES = 4;
+
+ public:
+ static ceph::unique_leakable_ptr<buffer::raw>
+ create(int mempool = mempool::mempool_buffer_anon)
+ {
+ const auto ZERO_AREA_SIZE = ZERO_AREA_NUM_PAGES * CEPH_PAGE_SIZE;
+ const auto [ptr, datalen] = alloc_data_n_controlblock(
+ ZERO_AREA_SIZE, /* align to */CEPH_PAGE_SIZE);
+ return ceph::unique_leakable_ptr<buffer::raw>(
+ new (ptr + datalen) raw_zeros(ptr, ZERO_AREA_SIZE, mempool));
+ }
+ };
+
class buffer::raw_malloc : public buffer::raw {
public:
MEMPOOL_CLASS_HELPERS();
return 0;
}
+ bool buffer::ptr::is_zero_fast() const
+ {
+ return dynamic_cast<const buffer::raw_zeros*>(_raw) != nullptr;
+ }
+
bool buffer::ptr::is_zero() const
{
- return mem_is_zero(c_str(), _len);
+ return /*is_zero_fast() ||*/ mem_is_zero(c_str(), _len);
}
unsigned buffer::ptr::append(char c)
return _buffers.back();
}
+ buffer::ptr buffer::list::always_zeroed_bptr() {
+ // See https://en.cppreference.com/w/cpp/language/storage_duration.html
+ // Section on static block variables states that since C++11 this is
+ // lazily evaluated and thread safe.
+ static ptr always_zeroed_bptr = raw_zeros::create();
+ return always_zeroed_bptr;
+ }
+
void buffer::list::append(const char *data, unsigned len)
{
_len += len;
_buffers.push_front(*bp.release());
}
+ void buffer::list::append_zero2(unsigned len)
+ {
+ _len += len;
+ while (len > 0) {
+ const auto round_size = std::min(len, always_zeroed_bptr().length());
+ auto bptr = ptr_node::create(always_zeroed_bptr(), 0, round_size);
+ _buffers.push_back(*bptr.release());
+ _num += 1;
+ len -= round_size;
+ }
+ }
+
void buffer::list::append_zero(unsigned len)
{
_len += len;
class raw_claimed_char;
class raw_unshareable; // diagnostic, unshareable char buffer
class raw_combined;
+ class raw_zeros;
class raw_claim_buffer;
unsigned wasted() const;
int cmp(const ptr& o) const;
+ /// is_zero_fast() is a variant aware about deduplicated zeros.
+ /// In Tentacle it shall NOT be used by anybody except ECBackend.
+ bool is_zero_fast() const;
bool is_zero() const;
// modifiers
return *_carriage;
}
+ ptr always_zeroed_bptr();
+
public:
// cons/des
list()
void append(std::istream& in);
contiguous_filler append_hole(unsigned len);
void append_zero(unsigned len);
+ /// append_zero2() is a temporary, short-living variant that deduplicates zeros.
+ /// In Tentacle it shall NOT be used by anybody except ECBackend.
+ /// In future release it will likely replace the append_zero() variant but
+ /// other changes at the interface are needed to make the transition safe.
+ void append_zero2(unsigned len);
void prepend_zero(unsigned len);
reserve_t obtain_contiguous_space(const unsigned len);
EXPECT_LE(1, af.cmp(acc));
}
+TEST(BufferPtr, is_zero_fast) {
+ // there is no easy way to create `raw_zeros` instances outside
+ // of bufferlist, thus use list::append_zero() to instantiate
+ buffer::list zeroed_bl;
+ zeroed_bl.append_zero2(42);
+ {
+ const auto& zeroed_bptr = zeroed_bl.front();
+ EXPECT_TRUE(zeroed_bptr.is_zero());
+ EXPECT_TRUE(zeroed_bptr.is_zero_fast());
+ }
+ {
+ buffer::ptr sub_zeroed_bptr(zeroed_bl.front(), 0, 42/2);
+ EXPECT_TRUE(sub_zeroed_bptr.is_zero());
+ EXPECT_TRUE(sub_zeroed_bptr.is_zero_fast());
+ }
+ {
+ buffer::ptr zeroed_empty_bptr(zeroed_bl.front(), 0, 0);
+ EXPECT_TRUE(zeroed_empty_bptr.is_zero());
+ EXPECT_TRUE(zeroed_empty_bptr.is_zero_fast());
+ }
+}
+
TEST(BufferPtr, is_zero) {
char str[2] = { '\0', 'X' };
{
const bufferptr ptr(buffer::create_static(2, str));
EXPECT_FALSE(ptr.is_zero());
+ EXPECT_FALSE(ptr.is_zero_fast());
}
{
const bufferptr ptr(buffer::create_static(1, str));
EXPECT_TRUE(ptr.is_zero());
+ EXPECT_FALSE(ptr.is_zero_fast());
+ }
+ {
+ const bufferptr ptr(buffer::create_static(0, str));
+ EXPECT_TRUE(ptr.is_zero());
+ EXPECT_FALSE(ptr.is_zero_fast());
}
}