ceph-mirror/src/test/bufferlist.cc
Jesse F. Williamson 132c77376f Add FoundationDB client library "libfdb".
Assisted-by: Codex:GPT-5

Signed-off-by: Jesse F. Williamson <jfw@ibm.com>
2026-06-30 09:06:20 -07:00

3144 lines
81 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:nil -*-
// vim: ts=8 sw=2 sts=2 expandtab
/*
* Ceph - scalable distributed file system
*
* Copyright (C) 2013 Cloudwatt <libre.licensing@cloudwatt.com>
*
* Author: Loic Dachary <loic@dachary.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library Public License for more details.
*
*/
#include <limits.h>
#include <errno.h>
#include <sys/uio.h>
#include <iostream> // for std::cout
#include "include/buffer.h"
#include "include/buffer_raw.h"
#include "include/compat.h"
#include "include/utime.h"
#include "include/coredumpctl.h"
#include "include/encoding.h"
#include "common/buffer_instrumentation.h"
#include "common/environment.h"
#include "common/Clock.h"
#include "common/safe_io.h"
#include "gtest/gtest.h"
#include "stdlib.h"
#include "fcntl.h"
#include "sys/stat.h"
#include "include/crc32c.h"
#include "common/sctp_crc32.h"
#define MAX_TEST 1000000
#define FILENAME "bufferlist"
#ifndef __has_feature
#define __has_feature(x) 0
#endif
#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
static constexpr bool bench_under_asan = true;
#else
static constexpr bool bench_under_asan = false;
#endif
static constexpr size_t asan_bench_rounds(size_t full) {
return bench_under_asan ? (full < 2 ? full : 2) : full;
}
using namespace std;
static char cmd[128];
using ceph::buffer_instrumentation::instrumented_bptr;
TEST(Buffer, constructors) {
unsigned len = 17;
//
// buffer::create
//
{
bufferptr ptr(buffer::create(len));
EXPECT_EQ(len, ptr.length());
}
//
// buffer::claim_char
//
{
char* str = new char[len];
::memset(str, 'X', len);
bufferptr ptr(buffer::claim_char(len, str));
EXPECT_EQ(len, ptr.length());
EXPECT_EQ(str, ptr.c_str());
EXPECT_EQ(0, ::memcmp(str, ptr.c_str(), len));
delete [] str;
}
//
// buffer::create_static
//
{
char* str = new char[len];
bufferptr ptr(buffer::create_static(len, str));
EXPECT_EQ(len, ptr.length());
EXPECT_EQ(str, ptr.c_str());
delete [] str;
}
//
// buffer::create_malloc
//
{
bufferptr ptr(buffer::create_malloc(len));
EXPECT_EQ(len, ptr.length());
// this doesn't throw on my x86_64 wheezy box --sage
//EXPECT_THROW(buffer::create_malloc((unsigned)ULLONG_MAX), buffer::bad_alloc);
}
//
// buffer::claim_malloc
//
{
char* str = (char*)malloc(len);
::memset(str, 'X', len);
bufferptr ptr(buffer::claim_malloc(len, str));
EXPECT_EQ(len, ptr.length());
EXPECT_EQ(str, ptr.c_str());
EXPECT_EQ(0, ::memcmp(str, ptr.c_str(), len));
}
//
// buffer::copy
//
{
const std::string expected(len, 'X');
bufferptr ptr(buffer::copy(expected.c_str(), expected.size()));
EXPECT_NE(expected.c_str(), ptr.c_str());
EXPECT_EQ(0, ::memcmp(expected.c_str(), ptr.c_str(), len));
}
//
// buffer::create_page_aligned
//
{
bufferptr ptr(buffer::create_page_aligned(len));
::memset(ptr.c_str(), 'X', len);
// doesn't throw on my x86_64 wheezy box --sage
//EXPECT_THROW(buffer::create_page_aligned((unsigned)ULLONG_MAX), buffer::bad_alloc);
#ifndef DARWIN
ASSERT_TRUE(ptr.is_page_aligned());
#endif // DARWIN
}
}
void bench_buffer_alloc(int size, int num)
{
utime_t start = ceph_clock_now();
for (int i=0; i<num; ++i) {
bufferptr p = buffer::create(size);
p.zero();
}
utime_t end = ceph_clock_now();
cout << num << " alloc of size " << size
<< " in " << (end - start) << std::endl;
}
TEST(Buffer, BenchAlloc) {
const int n = asan_bench_rounds(1000000);
bench_buffer_alloc(16384, n);
bench_buffer_alloc(4096, n);
bench_buffer_alloc(1024, n);
bench_buffer_alloc(256, n);
bench_buffer_alloc(32, n);
bench_buffer_alloc(4, n);
}
TEST(BufferRaw, ostream) {
bufferptr ptr(1);
std::ostringstream stream;
stream << *static_cast<instrumented_bptr&>(ptr).get_raw();
EXPECT_GT(stream.str().size(), stream.str().find("buffer::raw("));
EXPECT_GT(stream.str().size(), stream.str().find("len 1 nref 1)"));
}
//
// +-----------+ +-----+
// | | | |
// | offset +----------------+ |
// | | | |
// | length +---- | |
// | | \------- | |
// +-----------+ \---+ |
// | ptr | +-----+
// +-----------+ | raw |
// +-----+
//
TEST(BufferPtr, constructors) {
unsigned len = 17;
//
// ptr::ptr()
//
{
buffer::ptr ptr;
EXPECT_FALSE(ptr.have_raw());
EXPECT_EQ((unsigned)0, ptr.offset());
EXPECT_EQ((unsigned)0, ptr.length());
}
//
// ptr::ptr(raw *r)
//
{
bufferptr ptr(buffer::create(len));
EXPECT_TRUE(ptr.have_raw());
EXPECT_EQ((unsigned)0, ptr.offset());
EXPECT_EQ(len, ptr.length());
EXPECT_EQ(ptr.raw_length(), ptr.length());
EXPECT_EQ(1, ptr.raw_nref());
}
//
// ptr::ptr(unsigned l)
//
{
bufferptr ptr(len);
EXPECT_TRUE(ptr.have_raw());
EXPECT_EQ((unsigned)0, ptr.offset());
EXPECT_EQ(len, ptr.length());
EXPECT_EQ(1, ptr.raw_nref());
}
//
// ptr(const char *d, unsigned l)
//
{
const std::string str(len, 'X');
bufferptr ptr(str.c_str(), len);
EXPECT_TRUE(ptr.have_raw());
EXPECT_EQ((unsigned)0, ptr.offset());
EXPECT_EQ(len, ptr.length());
EXPECT_EQ(1, ptr.raw_nref());
EXPECT_EQ(0, ::memcmp(str.c_str(), ptr.c_str(), len));
}
//
// ptr(const ptr& p)
//
{
const std::string str(len, 'X');
bufferptr original(str.c_str(), len);
bufferptr ptr(original);
EXPECT_TRUE(ptr.have_raw());
EXPECT_EQ(static_cast<instrumented_bptr&>(original).get_raw(),
static_cast<instrumented_bptr&>(ptr).get_raw());
EXPECT_EQ(2, ptr.raw_nref());
EXPECT_EQ(0, ::memcmp(original.c_str(), ptr.c_str(), len));
}
//
// ptr(const ptr& p, unsigned o, unsigned l)
//
{
const std::string str(len, 'X');
bufferptr original(str.c_str(), len);
bufferptr ptr(original, 0, 0);
EXPECT_TRUE(ptr.have_raw());
EXPECT_EQ(static_cast<instrumented_bptr&>(original).get_raw(),
static_cast<instrumented_bptr&>(ptr).get_raw());
EXPECT_EQ(2, ptr.raw_nref());
EXPECT_EQ(0, ::memcmp(original.c_str(), ptr.c_str(), len));
PrCtl unset_dumpable;
EXPECT_DEATH(bufferptr(original, 0, original.length() + 1), "");
EXPECT_DEATH(bufferptr(bufferptr(), 0, 0), "");
}
//
// ptr(ptr&& p)
//
{
const std::string str(len, 'X');
bufferptr original(str.c_str(), len);
bufferptr ptr(std::move(original));
EXPECT_TRUE(ptr.have_raw());
EXPECT_FALSE(original.have_raw());
EXPECT_EQ(0, ::memcmp(str.c_str(), ptr.c_str(), len));
EXPECT_EQ(1, ptr.raw_nref());
}
}
TEST(BufferPtr, operator_assign) {
//
// ptr& operator= (const ptr& p)
//
bufferptr ptr(10);
ptr.copy_in(0, 3, "ABC");
char dest[1];
{
bufferptr copy = ptr;
copy.copy_out(1, 1, dest);
ASSERT_EQ('B', dest[0]);
}
//
// ptr& operator= (ptr&& p)
//
bufferptr move = std::move(ptr);
{
move.copy_out(1, 1, dest);
ASSERT_EQ('B', dest[0]);
}
EXPECT_FALSE(ptr.have_raw());
}
TEST(BufferPtr, assignment) {
unsigned len = 17;
//
// override a bufferptr set with the same raw
//
{
bufferptr original(len);
bufferptr same_raw(original);
unsigned offset = 5;
unsigned length = len - offset;
original.set_offset(offset);
original.set_length(length);
same_raw = original;
ASSERT_EQ(2, original.raw_nref());
ASSERT_EQ(static_cast<instrumented_bptr&>(same_raw).get_raw(),
static_cast<instrumented_bptr&>(original).get_raw());
ASSERT_EQ(same_raw.offset(), original.offset());
ASSERT_EQ(same_raw.length(), original.length());
}
//
// self assignment is a noop
//
{
bufferptr original(len);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wself-assign-overloaded"
original = original;
#pragma clang diagnostic pop
ASSERT_EQ(1, original.raw_nref());
ASSERT_EQ((unsigned)0, original.offset());
ASSERT_EQ(len, original.length());
}
//
// a copy points to the same raw
//
{
bufferptr original(len);
unsigned offset = 5;
unsigned length = len - offset;
original.set_offset(offset);
original.set_length(length);
bufferptr ptr;
ptr = original;
ASSERT_EQ(2, original.raw_nref());
ASSERT_EQ(static_cast<instrumented_bptr&>(ptr).get_raw(),
static_cast<instrumented_bptr&>(original).get_raw());
ASSERT_EQ(original.offset(), ptr.offset());
ASSERT_EQ(original.length(), ptr.length());
}
}
TEST(BufferPtr, swap) {
unsigned len = 17;
bufferptr ptr1(len);
::memset(ptr1.c_str(), 'X', len);
unsigned ptr1_offset = 4;
ptr1.set_offset(ptr1_offset);
unsigned ptr1_length = 3;
ptr1.set_length(ptr1_length);
bufferptr ptr2(len);
::memset(ptr2.c_str(), 'Y', len);
unsigned ptr2_offset = 5;
ptr2.set_offset(ptr2_offset);
unsigned ptr2_length = 7;
ptr2.set_length(ptr2_length);
ptr1.swap(ptr2);
EXPECT_EQ(ptr2_length, ptr1.length());
EXPECT_EQ(ptr2_offset, ptr1.offset());
EXPECT_EQ('Y', ptr1[0]);
EXPECT_EQ(ptr1_length, ptr2.length());
EXPECT_EQ(ptr1_offset, ptr2.offset());
EXPECT_EQ('X', ptr2[0]);
}
TEST(BufferPtr, release) {
unsigned len = 17;
bufferptr ptr1(len);
{
bufferptr ptr2(ptr1);
EXPECT_EQ(2, ptr1.raw_nref());
}
EXPECT_EQ(1, ptr1.raw_nref());
}
TEST(BufferPtr, have_raw) {
{
bufferptr ptr;
EXPECT_FALSE(ptr.have_raw());
}
{
bufferptr ptr(1);
EXPECT_TRUE(ptr.have_raw());
}
}
TEST(BufferPtr, is_n_page_sized) {
{
bufferptr ptr(CEPH_PAGE_SIZE);
EXPECT_TRUE(ptr.is_n_page_sized());
}
{
bufferptr ptr(1);
EXPECT_FALSE(ptr.is_n_page_sized());
}
}
TEST(BufferPtr, is_partial) {
bufferptr a;
EXPECT_FALSE(a.is_partial());
bufferptr b(10);
EXPECT_FALSE(b.is_partial());
bufferptr c(b, 1, 9);
EXPECT_TRUE(c.is_partial());
bufferptr d(b, 0, 9);
EXPECT_TRUE(d.is_partial());
}
TEST(BufferPtr, accessors) {
unsigned len = 17;
bufferptr ptr(len);
ptr.c_str()[0] = 'X';
ptr[1] = 'Y';
const bufferptr const_ptr(ptr);
EXPECT_NE((void*)nullptr, (void*)static_cast<instrumented_bptr&>(ptr).get_raw());
EXPECT_EQ('X', ptr.c_str()[0]);
{
bufferptr ptr;
PrCtl unset_dumpable;
EXPECT_DEATH(ptr.c_str(), "");
EXPECT_DEATH(ptr[0], "");
}
EXPECT_EQ('X', const_ptr.c_str()[0]);
{
const bufferptr const_ptr;
PrCtl unset_dumpable;
EXPECT_DEATH(const_ptr.c_str(), "");
EXPECT_DEATH(const_ptr[0], "");
}
EXPECT_EQ(len, const_ptr.length());
EXPECT_EQ((unsigned)0, const_ptr.offset());
EXPECT_EQ((unsigned)0, const_ptr.start());
EXPECT_EQ(len, const_ptr.end());
EXPECT_EQ(len, const_ptr.end());
{
bufferptr ptr(len);
unsigned unused = 1;
ptr.set_length(ptr.length() - unused);
EXPECT_EQ(unused, ptr.unused_tail_length());
}
{
bufferptr ptr;
EXPECT_EQ((unsigned)0, ptr.unused_tail_length());
}
{
PrCtl unset_dumpable;
EXPECT_DEATH(ptr[len], "");
EXPECT_DEATH(const_ptr[len], "");
}
{
const bufferptr const_ptr;
PrCtl unset_dumpable;
EXPECT_DEATH(const_ptr.raw_c_str(), "");
EXPECT_DEATH(const_ptr.raw_length(), "");
EXPECT_DEATH(const_ptr.raw_nref(), "");
}
EXPECT_NE((const char *)NULL, const_ptr.raw_c_str());
EXPECT_EQ(len, const_ptr.raw_length());
EXPECT_EQ(2, const_ptr.raw_nref());
{
bufferptr ptr(len);
unsigned wasted = 1;
ptr.set_length(ptr.length() - wasted * 2);
ptr.set_offset(wasted);
EXPECT_EQ(wasted * 2, ptr.wasted());
}
}
TEST(BufferPtr, cmp) {
bufferptr empty;
bufferptr a("A", 1);
bufferptr ab("AB", 2);
bufferptr af("AF", 2);
bufferptr acc("ACC", 3);
EXPECT_GE(-1, empty.cmp(a));
EXPECT_LE(1, a.cmp(empty));
EXPECT_GE(-1, a.cmp(ab));
EXPECT_LE(1, ab.cmp(a));
EXPECT_EQ(0, ab.cmp(ab));
EXPECT_GE(-1, ab.cmp(af));
EXPECT_LE(1, af.cmp(ab));
EXPECT_GE(-1, acc.cmp(af));
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());
}
}
TEST(BufferPtr, copy_out) {
{
const bufferptr ptr;
PrCtl unset_dumpable;
EXPECT_DEATH(ptr.copy_out((unsigned)0, (unsigned)0, NULL), "");
}
{
char in[] = "ABC";
const bufferptr ptr(buffer::create_static(strlen(in), in));
EXPECT_THROW(ptr.copy_out((unsigned)0, strlen(in) + 1, NULL), buffer::end_of_buffer);
EXPECT_THROW(ptr.copy_out(strlen(in) + 1, (unsigned)0, NULL), buffer::end_of_buffer);
char out[1] = { 'X' };
ptr.copy_out((unsigned)1, (unsigned)1, out);
EXPECT_EQ('B', out[0]);
}
}
TEST(BufferPtr, copy_out_bench) {
for (int s=1; s<=8; s*=2) {
utime_t start = ceph_clock_now();
int buflen = 1048576;
int count = asan_bench_rounds(1000);
uint64_t v;
for (int i=0; i<count; ++i) {
bufferptr bp(buflen);
for (int64_t j=0; j<buflen; j += s) {
bp.copy_out(j, s, (char *)&v);
}
}
utime_t end = ceph_clock_now();
cout << count << " fills of buffer len " << buflen
<< " with " << s << " byte copy_out in "
<< (end - start) << std::endl;
}
}
TEST(BufferPtr, copy_in) {
{
bufferptr ptr;
PrCtl unset_dumpable;
EXPECT_DEATH(ptr.copy_in((unsigned)0, (unsigned)0, NULL), "");
}
{
char in[] = "ABCD";
bufferptr ptr(2);
{
PrCtl unset_dumpable;
EXPECT_DEATH(ptr.copy_in((unsigned)0, strlen(in) + 1, NULL), "");
EXPECT_DEATH(ptr.copy_in(strlen(in) + 1, (unsigned)0, NULL), "");
}
ptr.copy_in((unsigned)0, (unsigned)2, in);
EXPECT_EQ(in[0], ptr[0]);
EXPECT_EQ(in[1], ptr[1]);
}
}
TEST(BufferPtr, copy_in_bench) {
for (int s=1; s<=8; s*=2) {
utime_t start = ceph_clock_now();
int buflen = 1048576;
int count = asan_bench_rounds(1000);
for (int i=0; i<count; ++i) {
bufferptr bp(buflen);
for (int64_t j=0; j<buflen; j += s) {
bp.copy_in(j, s, (char *)&j, false);
}
}
utime_t end = ceph_clock_now();
cout << count << " fills of buffer len " << buflen
<< " with " << s << " byte copy_in in "
<< (end - start) << std::endl;
}
}
TEST(BufferPtr, append) {
{
bufferptr ptr;
PrCtl unset_dumpable;
EXPECT_DEATH(ptr.append('A'), "");
EXPECT_DEATH(ptr.append("B", (unsigned)1), "");
}
{
bufferptr ptr(2);
{
PrCtl unset_dumpable;
EXPECT_DEATH(ptr.append('A'), "");
EXPECT_DEATH(ptr.append("B", (unsigned)1), "");
}
ptr.set_length(0);
ptr.append('A');
EXPECT_EQ((unsigned)1, ptr.length());
EXPECT_EQ('A', ptr[0]);
ptr.append("B", (unsigned)1);
EXPECT_EQ((unsigned)2, ptr.length());
EXPECT_EQ('B', ptr[1]);
}
}
TEST(BufferPtr, append_bench) {
char src[1048576];
memset(src, 0, sizeof(src));
for (int s=4; s<=16384; s*=4) {
utime_t start = ceph_clock_now();
int buflen = 1048576;
int count = asan_bench_rounds(4000);
for (int i=0; i<count; ++i) {
bufferptr bp(buflen);
bp.set_length(0);
for (int64_t j=0; j<buflen; j += s) {
bp.append(src + j, s);
}
}
utime_t end = ceph_clock_now();
cout << count << " fills of buffer len " << buflen
<< " with " << s << " byte appends in "
<< (end - start) << std::endl;
}
}
TEST(BufferPtr, zero) {
char str[] = "XXXX";
bufferptr ptr(buffer::create_static(strlen(str), str));
{
PrCtl unset_dumpable;
EXPECT_DEATH(ptr.zero(ptr.length() + 1, 0), "");
}
ptr.zero(1, 1);
EXPECT_EQ('X', ptr[0]);
EXPECT_EQ('\0', ptr[1]);
EXPECT_EQ('X', ptr[2]);
ptr.zero();
EXPECT_EQ('\0', ptr[0]);
}
TEST(BufferPtr, ostream) {
{
bufferptr ptr;
std::ostringstream stream;
stream << ptr;
EXPECT_GT(stream.str().size(), stream.str().find("buffer:ptr(0~0 no raw"));
}
{
char str[] = "XXXX";
bufferptr ptr(buffer::create_static(strlen(str), str));
std::ostringstream stream;
stream << ptr;
EXPECT_GT(stream.str().size(), stream.str().find("len 4 nref 1)"));
}
}
//
// +---------+
// | +-----+ |
// list ptr | | | |
// +----------+ +-----+ | | | |
// | append_ >-------> >--------------------> | |
// | buffer | +-----+ | | | |
// +----------+ ptr | | | |
// | _len | list +-----+ | | | |
// +----------+ +------+ ,--->+ >-----> | |
// | _buffers >----> >----- +-----+ | +-----+ |
// +----------+ +----^-+ \ ptr | raw |
// | last_p | / `-->+-----+ | +-----+ |
// +--------+-+ / + >-----> | |
// | ,- ,--->+-----+ | | | |
// | / ,--- | | | |
// | / ,--- | | | |
// +-v--+-^--+--^+-------+ | | | |
// | bl | ls | p | p_off >--------------->| | |
// +----+----+-----+-----+ | +-----+ |
// | | off >------------->| raw |
// +---------------+-----+ | |
// iterator +---------+
//
TEST(BufferListIterator, constructors) {
//
// iterator()
//
{
buffer::list::iterator i;
EXPECT_EQ((unsigned)0, i.get_off());
}
//
// iterator(list *l, unsigned o=0)
//
{
bufferlist bl;
bl.append("ABC", 3);
{
bufferlist::iterator i(&bl);
EXPECT_EQ((unsigned)0, i.get_off());
EXPECT_EQ('A', *i);
}
{
bufferlist::iterator i(&bl, 1);
EXPECT_EQ('B', *i);
EXPECT_EQ((unsigned)2, i.get_remaining());
}
}
//
// iterator(list *l, unsigned o, std::list<ptr>::iterator ip, unsigned po)
// not tested because of http://tracker.ceph.com/issues/4101
//
// iterator(const iterator& other)
//
{
bufferlist bl;
bl.append("ABC", 3);
bufferlist::iterator i(&bl, 1);
bufferlist::iterator j(i);
EXPECT_EQ(*i, *j);
++j;
EXPECT_NE(*i, *j);
EXPECT_EQ('B', *i);
EXPECT_EQ('C', *j);
bl.c_str()[1] = 'X';
}
//
// const_iterator(const iterator& other)
//
{
bufferlist bl;
bl.append("ABC", 3);
bufferlist::iterator i(&bl);
bufferlist::const_iterator ci(i);
EXPECT_EQ(0u, ci.get_off());
EXPECT_EQ('A', *ci);
}
}
TEST(BufferListIterator, empty_create_append_copy) {
bufferlist bl, bl2, bl3, out;
bl2.append("bar");
bl.swap(bl2);
bl2.append("xxx");
bl.append(bl2);
bl.rebuild();
bl.begin().copy(6, out);
ASSERT_TRUE(out.contents_equal(bl));
}
TEST(BufferListIterator, operator_assign) {
bufferlist bl;
bl.append("ABC", 3);
bufferlist::iterator i(&bl, 1);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wself-assign-overloaded"
i = i;
#pragma clang diagnostic pop
EXPECT_EQ('B', *i);
bufferlist::iterator j;
j = i;
EXPECT_EQ('B', *j);
}
TEST(BufferListIterator, get_off) {
bufferlist bl;
bl.append("ABC", 3);
bufferlist::iterator i(&bl, 1);
EXPECT_EQ((unsigned)1, i.get_off());
}
TEST(BufferListIterator, get_remaining) {
bufferlist bl;
bl.append("ABC", 3);
bufferlist::iterator i(&bl, 1);
EXPECT_EQ((unsigned)2, i.get_remaining());
}
TEST(BufferListIterator, end) {
bufferlist bl;
{
bufferlist::iterator i(&bl);
EXPECT_TRUE(i.end());
}
bl.append("ABC", 3);
{
bufferlist::iterator i(&bl);
EXPECT_FALSE(i.end());
}
}
static void bench_bufferlistiter_deref(const size_t step,
const size_t bufsize,
const size_t bufnum) {
const std::string buf(bufsize, 'a');
ceph::bufferlist bl;
for (size_t i = 0; i < bufnum; i++) {
bl.append(ceph::bufferptr(buf.c_str(), buf.size()));
}
utime_t start = ceph_clock_now();
bufferlist::iterator iter = bl.begin();
while (iter.get_remaining() >= step) {
iter += step;
}
utime_t end = ceph_clock_now();
cout << bufsize * bufnum << " derefs over bl with " << bufnum
<< " buffers, each " << bufsize << " bytes long"
<< " in " << (end - start) << std::endl;
}
TEST(BufferListIterator, BenchDeref) {
bench_bufferlistiter_deref(1, 1, asan_bench_rounds(4096000));
bench_bufferlistiter_deref(1, 10, asan_bench_rounds(409600));
bench_bufferlistiter_deref(1, 100, asan_bench_rounds(40960));
bench_bufferlistiter_deref(1, 1000, asan_bench_rounds(4096));
bench_bufferlistiter_deref(4, 1, asan_bench_rounds(1024000));
bench_bufferlistiter_deref(4, 10, asan_bench_rounds(102400));
bench_bufferlistiter_deref(4, 100, asan_bench_rounds(10240));
bench_bufferlistiter_deref(4, 1000, asan_bench_rounds(1024));
}
TEST(BufferListIterator, advance) {
bufferlist bl;
const std::string one("ABC");
bl.append(bufferptr(one.c_str(), one.size()));
const std::string two("DEF");
bl.append(bufferptr(two.c_str(), two.size()));
{
bufferlist::iterator i(&bl);
EXPECT_THROW(i += 200u, buffer::end_of_buffer);
}
{
bufferlist::iterator i(&bl);
EXPECT_EQ('A', *i);
i += 1u;
EXPECT_EQ('B', *i);
i += 3u;
EXPECT_EQ('E', *i);
}
}
TEST(BufferListIterator, iterate_with_empties) {
ceph::bufferlist bl;
EXPECT_EQ(bl.get_num_buffers(), 0u);
bl.push_back(ceph::buffer::create(0));
EXPECT_EQ(bl.length(), 0u);
EXPECT_EQ(bl.get_num_buffers(), 1u);
encode(int64_t(42), bl);
EXPECT_EQ(bl.get_num_buffers(), 2u);
bl.push_back(ceph::buffer::create(0));
EXPECT_EQ(bl.get_num_buffers(), 3u);
// append bufferlist with single, 0-sized ptr inside
{
ceph::bufferlist bl_with_empty_ptr;
bl_with_empty_ptr.push_back(ceph::buffer::create(0));
EXPECT_EQ(bl_with_empty_ptr.length(), 0u);
EXPECT_EQ(bl_with_empty_ptr.get_num_buffers(), 1u);
bl.append(bl_with_empty_ptr);
}
encode(int64_t(24), bl);
EXPECT_EQ(bl.get_num_buffers(), 5u);
auto i = bl.cbegin();
int64_t val;
decode(val, i);
EXPECT_EQ(val, 42l);
decode(val, i);
EXPECT_EQ(val, 24l);
val = 0;
i.seek(sizeof(val));
decode(val, i);
EXPECT_EQ(val, 24l);
EXPECT_TRUE(i == bl.end());
i.seek(0);
decode(val, i);
EXPECT_EQ(val, 42);
EXPECT_FALSE(i == bl.end());
}
TEST(BufferListIterator, get_ptr_and_advance)
{
bufferptr a("one", 3);
bufferptr b("two", 3);
bufferptr c("three", 5);
bufferlist bl;
bl.append(a);
bl.append(b);
bl.append(c);
const char *ptr;
bufferlist::iterator p = bl.begin();
ASSERT_EQ(3u, p.get_ptr_and_advance(11u, &ptr));
ASSERT_EQ(bl.length() - 3u, p.get_remaining());
ASSERT_EQ(0, memcmp(ptr, "one", 3));
ASSERT_EQ(2u, p.get_ptr_and_advance(2u, &ptr));
ASSERT_EQ(0, memcmp(ptr, "tw", 2));
ASSERT_EQ(1u, p.get_ptr_and_advance(4u, &ptr));
ASSERT_EQ(0, memcmp(ptr, "o", 1));
ASSERT_EQ(5u, p.get_ptr_and_advance(5u, &ptr));
ASSERT_EQ(0, memcmp(ptr, "three", 5));
ASSERT_EQ(0u, p.get_remaining());
}
TEST(BufferListIterator, iterator_crc32c) {
bufferlist bl1;
bufferlist bl2;
bufferlist bl3;
string s1(100, 'a');
string s2(50, 'b');
string s3(7, 'c');
string s;
bl1.append(s1);
bl1.append(s2);
bl1.append(s3);
s = s1 + s2 + s3;
bl2.append(s);
bufferlist::iterator it = bl2.begin();
ASSERT_EQ(bl1.crc32c(0), it.crc32c(it.get_remaining(), 0));
ASSERT_EQ(0u, it.get_remaining());
it = bl1.begin();
ASSERT_EQ(bl2.crc32c(0), it.crc32c(it.get_remaining(), 0));
bl3.append(s.substr(98, 55));
it = bl1.begin();
it += 98u;
ASSERT_EQ(bl3.crc32c(0), it.crc32c(55, 0));
ASSERT_EQ(4u, it.get_remaining());
bl3.clear();
bl3.append(s.substr(98 + 55));
it = bl1.begin();
it += 98u + 55u;
ASSERT_EQ(bl3.crc32c(0), it.crc32c(10, 0));
ASSERT_EQ(0u, it.get_remaining());
}
TEST(BufferListIterator, seek) {
bufferlist bl;
bl.append("ABC", 3);
bufferlist::iterator i(&bl, 1);
EXPECT_EQ('B', *i);
i.seek(2);
EXPECT_EQ('C', *i);
}
TEST(BufferListIterator, operator_star) {
bufferlist bl;
{
bufferlist::iterator i(&bl);
EXPECT_THROW(*i, buffer::end_of_buffer);
}
bl.append("ABC", 3);
{
bufferlist::iterator i(&bl);
EXPECT_EQ('A', *i);
EXPECT_THROW(i += 200u, buffer::end_of_buffer);
EXPECT_THROW(*i, buffer::end_of_buffer);
}
}
TEST(BufferListIterator, operator_equal) {
bufferlist bl;
bl.append("ABC", 3);
{
bufferlist::iterator i(&bl);
bufferlist::iterator j(&bl);
EXPECT_EQ(i, j);
}
{
bufferlist::const_iterator ci = bl.begin();
bufferlist::iterator i = bl.begin();
EXPECT_EQ(i, ci);
EXPECT_EQ(ci, i);
}
}
TEST(BufferListIterator, operator_nequal) {
bufferlist bl;
bl.append("ABC", 3);
{
bufferlist::iterator i(&bl);
bufferlist::iterator j(&bl);
EXPECT_NE(++i, j);
}
{
bufferlist::const_iterator ci = bl.begin();
bufferlist::const_iterator cj = bl.begin();
++ci;
EXPECT_NE(ci, cj);
bufferlist::iterator i = bl.begin();
EXPECT_NE(i, ci);
EXPECT_NE(ci, i);
}
{
// tests begin(), end(), operator++() also
string s("ABC");
int i = 0;
for (auto c : bl) {
EXPECT_EQ(s[i++], c);
}
}
}
TEST(BufferListIterator, operator_plus_plus) {
bufferlist bl;
{
bufferlist::iterator i(&bl);
EXPECT_THROW(++i, buffer::end_of_buffer);
}
bl.append("ABC", 3);
{
bufferlist::iterator i(&bl);
++i;
EXPECT_EQ('B', *i);
}
}
TEST(BufferListIterator, get_current_ptr) {
bufferlist bl;
{
bufferlist::iterator i(&bl);
EXPECT_THROW(++i, buffer::end_of_buffer);
}
bl.append("ABC", 3);
{
bufferlist::iterator i(&bl, 1);
const buffer::ptr ptr = i.get_current_ptr();
EXPECT_EQ('B', ptr[0]);
EXPECT_EQ((unsigned)1, ptr.offset());
EXPECT_EQ((unsigned)2, ptr.length());
}
}
TEST(BufferListIterator, copy) {
bufferlist bl;
const char *expected = "ABC";
bl.append(expected, 3);
//
// void copy(unsigned len, char *dest);
//
{
char* copy = (char*)malloc(3);
::memset(copy, 'X', 3);
bufferlist::iterator i(&bl);
//
// demonstrates that it seeks back to offset if p == ls->end()
//
EXPECT_THROW(i += 200u, buffer::end_of_buffer);
i.copy(2, copy);
EXPECT_EQ(0, ::memcmp(copy, expected, 2));
EXPECT_EQ('X', copy[2]);
i.seek(0);
i.copy(3, copy);
EXPECT_EQ(0, ::memcmp(copy, expected, 3));
free(copy);
}
//
// void copy(unsigned len, char *dest) via begin(size_t offset)
//
{
bufferlist bl;
EXPECT_THROW(bl.begin((unsigned)100).copy((unsigned)100, (char*)0), buffer::end_of_buffer);
const char *expected = "ABC";
bl.append(expected);
char *dest = new char[2];
bl.begin(1).copy(2, dest);
EXPECT_EQ(0, ::memcmp(expected + 1, dest, 2));
delete [] dest;
}
//
// void buffer::list::iterator::copy_deep(unsigned len, ptr &dest)
//
{
bufferptr ptr;
bufferlist::iterator i(&bl);
i.copy_deep(2, ptr);
EXPECT_EQ((unsigned)2, ptr.length());
EXPECT_EQ('A', ptr[0]);
EXPECT_EQ('B', ptr[1]);
}
//
// void buffer::list::iterator::copy_shallow(unsigned len, ptr &dest)
//
{
bufferptr ptr;
bufferlist::iterator i(&bl);
i.copy_shallow(2, ptr);
EXPECT_EQ((unsigned)2, ptr.length());
EXPECT_EQ('A', ptr[0]);
EXPECT_EQ('B', ptr[1]);
}
//
// void buffer::list::iterator::copy(unsigned len, list &dest)
//
{
bufferlist copy;
bufferlist::iterator i(&bl);
//
// demonstrates that it seeks back to offset if p == ls->end()
//
EXPECT_THROW(i += 200u, buffer::end_of_buffer);
i.copy(2, copy);
EXPECT_EQ(0, ::memcmp(copy.c_str(), expected, 2));
i.seek(0);
i.copy(3, copy);
EXPECT_EQ('A', copy[0]);
EXPECT_EQ('B', copy[1]);
EXPECT_EQ('A', copy[2]);
EXPECT_EQ('B', copy[3]);
EXPECT_EQ('C', copy[4]);
EXPECT_EQ((unsigned)(2 + 3), copy.length());
}
//
// void buffer::list::iterator::copy(unsigned len, list &dest) via begin(size_t offset)
//
{
bufferlist bl;
bufferlist dest;
EXPECT_THROW(bl.begin((unsigned)100).copy((unsigned)100, dest), buffer::end_of_buffer);
const char *expected = "ABC";
bl.append(expected);
bl.begin(1).copy(2, dest);
EXPECT_EQ(0, ::memcmp(expected + 1, dest.c_str(), 2));
}
//
// void buffer::list::iterator::copy_all(list &dest)
//
{
bufferlist copy;
bufferlist::iterator i(&bl);
//
// demonstrates that it seeks back to offset if p == ls->end()
//
EXPECT_THROW(i += 200u, buffer::end_of_buffer);
i.copy_all(copy);
EXPECT_EQ('A', copy[0]);
EXPECT_EQ('B', copy[1]);
EXPECT_EQ('C', copy[2]);
EXPECT_EQ((unsigned)3, copy.length());
}
//
// void copy(unsigned len, std::string &dest)
//
{
std::string copy;
bufferlist::iterator i(&bl);
//
// demonstrates that it seeks back to offset if p == ls->end()
//
EXPECT_THROW(i += 200u, buffer::end_of_buffer);
i.copy(2, copy);
EXPECT_EQ(0, ::memcmp(copy.c_str(), expected, 2));
i.seek(0);
i.copy(3, copy);
EXPECT_EQ('A', copy[0]);
EXPECT_EQ('B', copy[1]);
EXPECT_EQ('A', copy[2]);
EXPECT_EQ('B', copy[3]);
EXPECT_EQ('C', copy[4]);
EXPECT_EQ((unsigned)(2 + 3), copy.length());
}
//
// void copy(unsigned len, std::string &dest) via begin(size_t offset)
//
{
bufferlist bl;
std::string dest;
EXPECT_THROW(bl.begin((unsigned)100).copy((unsigned)100, dest), buffer::end_of_buffer);
const char *expected = "ABC";
bl.append(expected);
bl.begin(1).copy(2, dest);
EXPECT_EQ(0, ::memcmp(expected + 1, dest.c_str(), 2));
}
}
TEST(BufferListIterator, copy_in) {
bufferlist bl;
const char *existing = "XXX";
bl.append(existing, 3);
//
// void buffer::list::iterator::copy_in(unsigned len, const char *src)
//
{
bufferlist::iterator i(&bl);
//
// demonstrates that it seeks back to offset if p == ls->end()
//
EXPECT_THROW(i += 200u, buffer::end_of_buffer);
const char *expected = "ABC";
i.copy_in(3, expected);
EXPECT_EQ(0, ::memcmp(bl.c_str(), expected, 3));
EXPECT_EQ('A', bl[0]);
EXPECT_EQ('B', bl[1]);
EXPECT_EQ('C', bl[2]);
EXPECT_EQ((unsigned)3, bl.length());
}
//
// void copy_in(unsigned len, const char *src) via begin(size_t offset)
//
{
bufferlist bl;
bl.append("XXX");
EXPECT_THROW(bl.begin((unsigned)100).copy_in((unsigned)100, (char*)0), buffer::end_of_buffer);
bl.begin(1).copy_in(2, "AB");
EXPECT_EQ(0, ::memcmp("XAB", bl.c_str(), 3));
}
//
// void buffer::list::iterator::copy_in(unsigned len, const list& otherl)
//
{
bufferlist::iterator i(&bl);
//
// demonstrates that it seeks back to offset if p == ls->end()
//
EXPECT_THROW(i += 200u, buffer::end_of_buffer);
bufferlist expected;
expected.append("ABC", 3);
i.copy_in(3, expected);
EXPECT_EQ(0, ::memcmp(bl.c_str(), expected.c_str(), 3));
EXPECT_EQ('A', bl[0]);
EXPECT_EQ('B', bl[1]);
EXPECT_EQ('C', bl[2]);
EXPECT_EQ((unsigned)3, bl.length());
}
//
// void copy_in(unsigned len, const list& src) via begin(size_t offset)
//
{
bufferlist bl;
bl.append("XXX");
bufferlist src;
src.append("ABC");
EXPECT_THROW(bl.begin((unsigned)100).copy_in((unsigned)100, src), buffer::end_of_buffer);
bl.begin(1).copy_in(2, src);
EXPECT_EQ(0, ::memcmp("XAB", bl.c_str(), 3));
}
}
// iterator& buffer::list::const_iterator::operator++()
TEST(BufferListConstIterator, operator_plus_plus) {
bufferlist bl;
{
bufferlist::const_iterator i(&bl);
EXPECT_THROW(++i, buffer::end_of_buffer);
}
bl.append("ABC", 3);
{
const bufferlist const_bl(bl);
bufferlist::const_iterator i(const_bl.begin());
++i;
EXPECT_EQ('B', *i);
}
}
TEST(BufferList, constructors) {
//
// list()
//
{
bufferlist bl;
ASSERT_EQ((unsigned)0, bl.length());
}
//
// list(unsigned prealloc)
//
{
bufferlist bl(1);
ASSERT_EQ((unsigned)0, bl.length());
bl.append('A');
ASSERT_EQ('A', bl[0]);
}
//
// list(const list& other)
//
{
bufferlist bl(1);
bl.append('A');
ASSERT_EQ('A', bl[0]);
bufferlist copy(bl);
ASSERT_EQ('A', copy[0]);
}
//
// list(list&& other)
//
{
bufferlist bl(1);
bl.append('A');
bufferlist copy = std::move(bl);
ASSERT_EQ(0U, bl.length());
ASSERT_EQ(1U, copy.length());
ASSERT_EQ('A', copy[0]);
}
}
TEST(BufferList, append_after_move) {
bufferlist bl(6);
bl.append("ABC", 3);
EXPECT_EQ(1, bl.get_num_buffers());
bufferlist moved_to_bl(std::move(bl));
moved_to_bl.append("123", 3);
// it's expected that the list(list&&) ctor will preserve the _carriage
EXPECT_EQ(1, moved_to_bl.get_num_buffers());
EXPECT_EQ(0, ::memcmp("ABC123", moved_to_bl.c_str(), 6));
}
void bench_bufferlist_alloc(int size, int num, int per)
{
utime_t start = ceph_clock_now();
for (int i=0; i<num; ++i) {
bufferlist bl;
for (int j=0; j<per; ++j)
bl.push_back(buffer::ptr_node::create(buffer::create(size)));
}
utime_t end = ceph_clock_now();
cout << num << " alloc of size " << size
<< " in " << (end - start) << std::endl;
}
TEST(BufferList, BenchAlloc) {
const int n = asan_bench_rounds(100000);
bench_bufferlist_alloc(32768, n, 16);
bench_bufferlist_alloc(25000, n, 16);
bench_bufferlist_alloc(16384, n, 16);
bench_bufferlist_alloc(10000, n, 16);
bench_bufferlist_alloc(8192, n, 16);
bench_bufferlist_alloc(6000, n, 16);
bench_bufferlist_alloc(4096, n, 16);
bench_bufferlist_alloc(1024, n, 16);
bench_bufferlist_alloc(256, n, 16);
bench_bufferlist_alloc(32, n, 16);
bench_bufferlist_alloc(4, n, 16);
}
/*
* append_bench tests now have multiple variants:
*
* Version 1 tests allocate a single bufferlist during loop iteration.
* Ultimately very little memory is utilized since the bufferlist immediately
* drops out of scope. This was the original variant of these tests but showed
* unexpected performance characteristics that appears to be tied to tcmalloc
* and/or kernel behavior depending on the bufferlist size and step size.
*
* Version 2 tests allocate a configurable number of bufferlists that are
* replaced round-robin during loop iteration. Version 2 tests are designed
* to better mimic performance when multiple bufferlists are in memory at the
* same time. During testing this showed more consistent and seemingly
* accurate behavior across bufferlist and step sizes.
*/
TEST(BufferList, append_bench_with_size_hint) {
std::array<char, 1048576> src = { 0, };
for (size_t step = 4; step <= 16384; step *= 4) {
const utime_t start = ceph_clock_now();
constexpr size_t rounds = asan_bench_rounds(4000);
for (size_t r = 0; r < rounds; ++r) {
ceph::bufferlist bl(std::size(src));
for (auto iter = std::begin(src);
iter != std::end(src);
iter = std::next(iter, step)) {
bl.append(&*iter, step);
}
}
cout << rounds << " fills of buffer len " << src.size()
<< " with " << step << " byte appends in "
<< (ceph_clock_now() - start) << std::endl;
}
}
TEST(BufferList, append_bench_with_size_hint2) {
std::array<char, 1048576> src = { 0, };
constexpr size_t rounds = asan_bench_rounds(4000);
constexpr int conc_bl = 400;
std::vector<ceph::bufferlist*> bls(conc_bl);
for (int i = 0; i < conc_bl; i++) {
bls[i] = new ceph::bufferlist;
}
for (size_t step = 4; step <= 16384; step *= 4) {
const utime_t start = ceph_clock_now();
for (size_t r = 0; r < rounds; ++r) {
delete bls[r % conc_bl];
bls[r % conc_bl] = new ceph::bufferlist(std::size(src));
for (auto iter = std::begin(src);
iter != std::end(src);
iter = std::next(iter, step)) {
bls[r % conc_bl]->append(&*iter, step);
}
}
cout << rounds << " fills of buffer len " << src.size()
<< " with " << step << " byte appends in "
<< (ceph_clock_now() - start) << std::endl;
}
for (int i = 0; i < conc_bl; i++) {
delete bls[i];
}
}
TEST(BufferList, append_bench) {
std::array<char, 1048576> src = { 0, };
for (size_t step = 4; step <= 16384; step *= 4) {
const utime_t start = ceph_clock_now();
constexpr size_t rounds = asan_bench_rounds(4000);
for (size_t r = 0; r < rounds; ++r) {
ceph::bufferlist bl;
for (auto iter = std::begin(src);
iter != std::end(src);
iter = std::next(iter, step)) {
bl.append(&*iter, step);
}
}
cout << rounds << " fills of buffer len " << src.size()
<< " with " << step << " byte appends in "
<< (ceph_clock_now() - start) << std::endl;
}
}
TEST(BufferList, append_bench2) {
std::array<char, 1048576> src = { 0, };
constexpr size_t rounds = asan_bench_rounds(4000);
constexpr int conc_bl = 400;
std::vector<ceph::bufferlist*> bls(conc_bl);
for (int i = 0; i < conc_bl; i++) {
bls[i] = new ceph::bufferlist;
}
for (size_t step = 4; step <= 16384; step *= 4) {
const utime_t start = ceph_clock_now();
for (size_t r = 0; r < rounds; ++r) {
delete bls[r % conc_bl];
bls[r % conc_bl] = new ceph::bufferlist;
for (auto iter = std::begin(src);
iter != std::end(src);
iter = std::next(iter, step)) {
bls[r % conc_bl]->append(&*iter, step);
}
}
cout << rounds << " fills of buffer len " << src.size()
<< " with " << step << " byte appends in "
<< (ceph_clock_now() - start) << std::endl;
}
for (int i = 0; i < conc_bl; i++) {
delete bls[i];
}
}
TEST(BufferList, append_hole_bench) {
constexpr size_t targeted_bl_size = 1048576;
for (size_t step = 512; step <= 65536; step *= 2) {
const utime_t start = ceph_clock_now();
constexpr size_t rounds = asan_bench_rounds(80000);
for (size_t r = 0; r < rounds; ++r) {
ceph::bufferlist bl;
while (bl.length() < targeted_bl_size) {
bl.append_hole(step);
}
}
cout << rounds << " fills of buffer len " << targeted_bl_size
<< " with " << step << " byte long append_hole in "
<< (ceph_clock_now() - start) << std::endl;
}
}
TEST(BufferList, append_hole_bench2) {
constexpr size_t targeted_bl_size = 1048576;
constexpr size_t rounds = asan_bench_rounds(80000);
constexpr int conc_bl = 400;
std::vector<ceph::bufferlist*> bls(conc_bl);
for (int i = 0; i < conc_bl; i++) {
bls[i] = new ceph::bufferlist;
}
for (size_t step = 512; step <= 65536; step *= 2) {
const utime_t start = ceph_clock_now();
for (size_t r = 0; r < rounds; ++r) {
delete bls[r % conc_bl];
bls[r % conc_bl] = new ceph::bufferlist;
while (bls[r % conc_bl]->length() < targeted_bl_size) {
bls[r % conc_bl]->append_hole(step);
}
}
cout << rounds << " fills of buffer len " << targeted_bl_size
<< " with " << step << " byte long append_hole in "
<< (ceph_clock_now() - start) << std::endl;
}
for (int i = 0; i < conc_bl; i++) {
delete bls[i];
}
}
TEST(BufferList, operator_assign_rvalue) {
bufferlist from;
{
bufferptr ptr(2);
from.append(ptr);
}
bufferlist to;
{
bufferptr ptr(4);
to.append(ptr);
}
EXPECT_EQ((unsigned)4, to.length());
EXPECT_EQ((unsigned)1, to.get_num_buffers());
to = std::move(from);
EXPECT_EQ((unsigned)2, to.length());
EXPECT_EQ((unsigned)1, to.get_num_buffers());
EXPECT_EQ((unsigned)0, from.get_num_buffers());
EXPECT_EQ((unsigned)0, from.length());
}
TEST(BufferList, operator_equal) {
//
// list& operator= (const list& other)
//
bufferlist bl;
bl.append("ABC", 3);
{
std::string dest;
bl.begin(1).copy(1, dest);
ASSERT_EQ('B', dest[0]);
}
{
bufferlist copy = bl;
std::string dest;
copy.begin(1).copy(1, dest);
ASSERT_EQ('B', dest[0]);
}
//
// list& operator= (list&& other)
//
bufferlist move;
move = std::move(bl);
{
std::string dest;
move.begin(1).copy(1, dest);
ASSERT_EQ('B', dest[0]);
}
EXPECT_TRUE(move.length());
EXPECT_TRUE(!bl.length());
}
TEST(BufferList, buffers) {
bufferlist bl;
ASSERT_EQ((unsigned)0, bl.get_num_buffers());
bl.append('A');
ASSERT_EQ((unsigned)1, bl.get_num_buffers());
}
TEST(BufferList, to_str) {
{
bufferlist bl;
bl.append("foo");
ASSERT_EQ(bl.to_str(), string("foo"));
}
{
bufferptr a("foobarbaz", 9);
bufferptr b("123456789", 9);
bufferptr c("ABCDEFGHI", 9);
bufferlist bl;
bl.append(a);
bl.append(b);
bl.append(c);
ASSERT_EQ(bl.to_str(), string("foobarbaz123456789ABCDEFGHI"));
}
}
TEST(BufferList, swap) {
bufferlist b1;
b1.append('A');
bufferlist b2;
b2.append('B');
b1.swap(b2);
std::string s1;
b1.begin().copy(1, s1);
ASSERT_EQ('B', s1[0]);
std::string s2;
b2.begin().copy(1, s2);
ASSERT_EQ('A', s2[0]);
}
TEST(BufferList, length) {
bufferlist bl;
ASSERT_EQ((unsigned)0, bl.length());
bl.append('A');
ASSERT_EQ((unsigned)1, bl.length());
}
TEST(BufferList, contents_equal) {
//
// A BB
// AB B
//
bufferlist bl1;
bl1.append("A");
bl1.append("BB");
bufferlist bl2;
ASSERT_FALSE(bl1.contents_equal(bl2)); // different length
bl2.append("AB");
bl2.append("B");
ASSERT_TRUE(bl1.contents_equal(bl2)); // same length same content
//
// ABC
//
bufferlist bl3;
bl3.append("ABC");
ASSERT_FALSE(bl1.contents_equal(bl3)); // same length different content
}
TEST(BufferList, is_aligned) {
const int SIMD_ALIGN = 64;
{
bufferlist bl;
EXPECT_TRUE(bl.is_aligned(SIMD_ALIGN));
}
{
bufferlist bl;
bufferptr ptr(buffer::create_aligned(2, SIMD_ALIGN));
ptr.set_offset(1);
ptr.set_length(1);
bl.append(ptr);
EXPECT_FALSE(bl.is_aligned(SIMD_ALIGN));
bl.rebuild_aligned(SIMD_ALIGN);
EXPECT_TRUE(bl.is_aligned(SIMD_ALIGN));
}
{
bufferlist bl;
bufferptr ptr(buffer::create_aligned(SIMD_ALIGN + 1, SIMD_ALIGN));
ptr.set_offset(1);
ptr.set_length(SIMD_ALIGN);
bl.append(ptr);
EXPECT_FALSE(bl.is_aligned(SIMD_ALIGN));
bl.rebuild_aligned(SIMD_ALIGN);
EXPECT_TRUE(bl.is_aligned(SIMD_ALIGN));
}
}
TEST(BufferList, is_n_align_sized) {
const int SIMD_ALIGN = 64;
{
bufferlist bl;
EXPECT_TRUE(bl.is_n_align_sized(SIMD_ALIGN));
}
{
bufferlist bl;
bl.append_zero(1);
EXPECT_FALSE(bl.is_n_align_sized(SIMD_ALIGN));
}
{
bufferlist bl;
bl.append_zero(SIMD_ALIGN);
EXPECT_TRUE(bl.is_n_align_sized(SIMD_ALIGN));
}
}
TEST(BufferList, is_page_aligned) {
{
bufferlist bl;
EXPECT_TRUE(bl.is_page_aligned());
}
{
bufferlist bl;
bufferptr ptr(buffer::create_page_aligned(2));
ptr.set_offset(1);
ptr.set_length(1);
bl.append(ptr);
EXPECT_FALSE(bl.is_page_aligned());
bl.rebuild_page_aligned();
EXPECT_TRUE(bl.is_page_aligned());
}
{
bufferlist bl;
bufferptr ptr(buffer::create_page_aligned(CEPH_PAGE_SIZE + 1));
ptr.set_offset(1);
ptr.set_length(CEPH_PAGE_SIZE);
bl.append(ptr);
EXPECT_FALSE(bl.is_page_aligned());
bl.rebuild_page_aligned();
EXPECT_TRUE(bl.is_page_aligned());
}
}
TEST(BufferList, is_n_page_sized) {
{
bufferlist bl;
EXPECT_TRUE(bl.is_n_page_sized());
}
{
bufferlist bl;
bl.append_zero(1);
EXPECT_FALSE(bl.is_n_page_sized());
}
{
bufferlist bl;
bl.append_zero(CEPH_PAGE_SIZE);
EXPECT_TRUE(bl.is_n_page_sized());
}
}
TEST(BufferList, page_aligned_appender) {
bufferlist bl;
{
auto a = bl.get_page_aligned_appender(5);
a.append("asdf", 4);
cout << bl << std::endl;
ASSERT_EQ(1u, bl.get_num_buffers());
ASSERT_TRUE(bl.contents_equal("asdf", 4));
a.append("asdf", 4);
for (unsigned n = 0; n < 3 * CEPH_PAGE_SIZE; ++n) {
a.append("x", 1);
}
cout << bl << std::endl;
ASSERT_EQ(1u, bl.get_num_buffers());
// verify the beginning
{
bufferlist t;
t.substr_of(bl, 0, 10);
ASSERT_TRUE(t.contents_equal("asdfasdfxx", 10));
}
for (unsigned n = 0; n < 3 * CEPH_PAGE_SIZE; ++n) {
a.append("y", 1);
}
cout << bl << std::endl;
ASSERT_EQ(2u, bl.get_num_buffers());
a.append_zero(42);
// ensure append_zero didn't introduce a fragmentation
ASSERT_EQ(2u, bl.get_num_buffers());
// verify the end is actually zeroed
{
bufferlist t;
t.substr_of(bl, bl.length() - 42, 42);
ASSERT_TRUE(t.is_zero());
}
// let's check whether appending a bufferlist directly to `bl`
// doesn't fragment further C string appends via appender.
{
const auto& initial_back = bl.back();
{
bufferlist src;
src.append("abc", 3);
bl.claim_append(src);
// surely the extra `ptr_node` taken from `src` must get
// reflected in the `bl` instance
ASSERT_EQ(3u, bl.get_num_buffers());
}
// moreover, the next C string-taking `append()` had to
// create anoter `ptr_node` instance but...
a.append("xyz", 3);
ASSERT_EQ(4u, bl.get_num_buffers());
// ... it should point to the same `buffer::raw` instance
// (to the same same block of memory).
ASSERT_EQ(bl.back().raw_c_str(), initial_back.raw_c_str());
}
// check whether it'll take the first byte only and whether
// the auto-flushing works.
for (unsigned n = 0; n < 10 * CEPH_PAGE_SIZE - 3; ++n) {
a.append("zasdf", 1);
}
}
{
cout << bl << std::endl;
ASSERT_EQ(6u, bl.get_num_buffers());
}
// Verify that `page_aligned_appender` does respect the carrying
// `_carriage` over multiple allocations. Although `append_zero()`
// is used here, this affects other members of the append family.
// This part would be crucial for e.g. `encode()`.
{
bl.append_zero(42);
cout << bl << std::endl;
ASSERT_EQ(6u, bl.get_num_buffers());
}
}
TEST(BufferList, rebuild_aligned_size_and_memory) {
const unsigned SIMD_ALIGN = 64;
const unsigned BUFFER_SIZE = 67;
bufferlist bl;
// These two must be concatenated into one memory + size aligned
// bufferptr
{
bufferptr ptr(buffer::create_aligned(2, SIMD_ALIGN));
ptr.set_offset(1);
ptr.set_length(1);
bl.append(ptr);
}
{
bufferptr ptr(buffer::create_aligned(BUFFER_SIZE - 1, SIMD_ALIGN));
bl.append(ptr);
}
// This one must be left alone
{
bufferptr ptr(buffer::create_aligned(BUFFER_SIZE, SIMD_ALIGN));
bl.append(ptr);
}
// These two must be concatenated into one memory + size aligned
// bufferptr
{
bufferptr ptr(buffer::create_aligned(2, SIMD_ALIGN));
ptr.set_offset(1);
ptr.set_length(1);
bl.append(ptr);
}
{
bufferptr ptr(buffer::create_aligned(BUFFER_SIZE - 1, SIMD_ALIGN));
bl.append(ptr);
}
EXPECT_FALSE(bl.is_aligned(SIMD_ALIGN));
EXPECT_FALSE(bl.is_n_align_sized(BUFFER_SIZE));
EXPECT_EQ(BUFFER_SIZE * 3, bl.length());
EXPECT_FALSE(bl.front().is_aligned(SIMD_ALIGN));
EXPECT_FALSE(bl.front().is_n_align_sized(BUFFER_SIZE));
EXPECT_EQ(5U, bl.get_num_buffers());
bl.rebuild_aligned_size_and_memory(BUFFER_SIZE, SIMD_ALIGN);
EXPECT_TRUE(bl.is_aligned(SIMD_ALIGN));
EXPECT_TRUE(bl.is_n_align_sized(BUFFER_SIZE));
EXPECT_EQ(3U, bl.get_num_buffers());
bl.rebuild_aligned_size_and_memory(BUFFER_SIZE, SIMD_ALIGN, 1);
EXPECT_EQ(1U, bl.get_num_buffers());
{
/* bug replicator, to test rebuild_aligned_size_and_memory() in the
* scenario where the first bptr is both size and memory aligned and
* the second is 0-length */
bl.clear();
bl.append(bufferptr{buffer::create_aligned(4096, 4096)});
bufferptr ptr(buffer::create_aligned(42, 4096));
/* bl.back().length() must be 0. offset set to 42 guarantees
* the entire list is unaligned. */
bl.append(ptr, 42, 0);
EXPECT_EQ(bl.get_num_buffers(), 2);
EXPECT_EQ(bl.back().length(), 0);
EXPECT_FALSE(bl.is_aligned(4096));
/* rebuild_aligned() calls rebuild_aligned_size_and_memory().
* we assume the rebuild always happens. */
EXPECT_TRUE(bl.rebuild_aligned(4096));
EXPECT_EQ(bl.get_num_buffers(), 1);
}
}
TEST(BufferList, is_zero) {
{
bufferlist bl;
EXPECT_TRUE(bl.is_zero());
}
{
bufferlist bl;
bl.append('A');
EXPECT_FALSE(bl.is_zero());
}
{
bufferlist bl;
bl.append_zero(1);
EXPECT_TRUE(bl.is_zero());
}
for (size_t i = 1; i <= 256; ++i) {
bufferlist bl;
bl.append_zero(i);
EXPECT_TRUE(bl.is_zero());
bl.append('A');
// ensure buffer is a single, contiguous before testing
bl.rebuild();
EXPECT_FALSE(bl.is_zero());
}
}
TEST(BufferList, clear) {
bufferlist bl;
unsigned len = 17;
bl.append_zero(len);
bl.clear();
EXPECT_EQ((unsigned)0, bl.length());
EXPECT_EQ((unsigned)0, bl.get_num_buffers());
}
TEST(BufferList, push_back) {
//
// void push_back(ptr& bp)
//
{
bufferlist bl;
bufferptr ptr;
bl.push_back(ptr);
EXPECT_EQ((unsigned)0, bl.length());
EXPECT_EQ((unsigned)0, bl.get_num_buffers());
}
unsigned len = 17;
{
bufferlist bl;
bl.append('A');
bufferptr ptr(len);
ptr.c_str()[0] = 'B';
bl.push_back(ptr);
EXPECT_EQ((unsigned)(1 + len), bl.length());
EXPECT_EQ((unsigned)2, bl.get_num_buffers());
EXPECT_EQ('B', bl.back()[0]);
const bufferptr& back_bp = bl.back();
EXPECT_EQ(static_cast<instrumented_bptr&>(ptr).get_raw(),
static_cast<const instrumented_bptr&>(back_bp).get_raw());
}
//
// void push_back(ptr&& bp)
//
{
bufferlist bl;
bufferptr ptr;
bl.push_back(std::move(ptr));
EXPECT_EQ((unsigned)0, bl.length());
EXPECT_EQ((unsigned)0, bl.get_num_buffers());
}
{
bufferlist bl;
bl.append('A');
bufferptr ptr(len);
ptr.c_str()[0] = 'B';
bl.push_back(std::move(ptr));
EXPECT_EQ((unsigned)(1 + len), bl.length());
EXPECT_EQ((unsigned)2, bl.get_num_buffers());
EXPECT_EQ('B', bl.buffers().back()[0]);
EXPECT_FALSE(static_cast<instrumented_bptr&>(ptr).get_raw());
}
}
TEST(BufferList, is_contiguous) {
bufferlist bl;
EXPECT_TRUE(bl.is_contiguous());
EXPECT_EQ((unsigned)0, bl.get_num_buffers());
bl.append('A');
EXPECT_TRUE(bl.is_contiguous());
EXPECT_EQ((unsigned)1, bl.get_num_buffers());
bufferptr ptr(1);
bl.push_back(ptr);
EXPECT_FALSE(bl.is_contiguous());
EXPECT_EQ((unsigned)2, bl.get_num_buffers());
}
TEST(BufferList, rebuild) {
{
bufferlist bl;
bufferptr ptr(buffer::create_page_aligned(2));
ptr[0] = 'X';
ptr[1] = 'Y';
ptr.set_offset(1);
ptr.set_length(1);
bl.append(ptr);
EXPECT_FALSE(bl.is_page_aligned());
bl.rebuild();
EXPECT_EQ(1U, bl.length());
EXPECT_EQ('Y', *bl.begin());
}
{
bufferlist bl;
const std::string str(CEPH_PAGE_SIZE, 'X');
bl.append(str.c_str(), str.size());
bl.append(str.c_str(), str.size());
EXPECT_EQ((unsigned)2, bl.get_num_buffers());
//EXPECT_TRUE(bl.is_aligned(CEPH_BUFFER_APPEND_SIZE));
bl.rebuild();
EXPECT_TRUE(bl.is_page_aligned());
EXPECT_EQ((unsigned)1, bl.get_num_buffers());
}
{
bufferlist bl;
char t1[] = "X";
bufferlist a2;
a2.append(t1, 1);
bl.rebuild();
bl.append(a2);
EXPECT_EQ((unsigned)1, bl.length());
bufferlist::iterator p = bl.begin();
char dst[1];
p.copy(1, dst);
EXPECT_EQ(0, memcmp(dst, "X", 1));
}
}
TEST(BufferList, rebuild_page_aligned) {
{
bufferlist bl;
{
bufferptr ptr(buffer::create_page_aligned(CEPH_PAGE_SIZE + 1));
ptr.set_offset(1);
ptr.set_length(CEPH_PAGE_SIZE);
bl.append(ptr);
}
EXPECT_EQ((unsigned)1, bl.get_num_buffers());
EXPECT_FALSE(bl.is_page_aligned());
bl.rebuild_page_aligned();
EXPECT_TRUE(bl.is_page_aligned());
EXPECT_EQ((unsigned)1, bl.get_num_buffers());
}
{
bufferlist bl;
bufferptr ptr(buffer::create_page_aligned(1));
char *p = ptr.c_str();
bl.append(ptr);
bl.rebuild_page_aligned();
EXPECT_EQ(p, bl.front().c_str());
}
{
bufferlist bl;
{
bufferptr ptr(buffer::create_page_aligned(CEPH_PAGE_SIZE));
EXPECT_TRUE(ptr.is_page_aligned());
EXPECT_TRUE(ptr.is_n_page_sized());
bl.append(ptr);
}
{
bufferptr ptr(buffer::create_page_aligned(CEPH_PAGE_SIZE + 1));
EXPECT_TRUE(ptr.is_page_aligned());
EXPECT_FALSE(ptr.is_n_page_sized());
bl.append(ptr);
}
{
bufferptr ptr(buffer::create_page_aligned(2));
ptr.set_offset(1);
ptr.set_length(1);
EXPECT_FALSE(ptr.is_page_aligned());
EXPECT_FALSE(ptr.is_n_page_sized());
bl.append(ptr);
}
{
bufferptr ptr(buffer::create_page_aligned(CEPH_PAGE_SIZE - 2));
EXPECT_TRUE(ptr.is_page_aligned());
EXPECT_FALSE(ptr.is_n_page_sized());
bl.append(ptr);
}
{
bufferptr ptr(buffer::create_page_aligned(CEPH_PAGE_SIZE));
EXPECT_TRUE(ptr.is_page_aligned());
EXPECT_TRUE(ptr.is_n_page_sized());
bl.append(ptr);
}
{
bufferptr ptr(buffer::create_page_aligned(CEPH_PAGE_SIZE + 1));
ptr.set_offset(1);
ptr.set_length(CEPH_PAGE_SIZE);
EXPECT_FALSE(ptr.is_page_aligned());
EXPECT_TRUE(ptr.is_n_page_sized());
bl.append(ptr);
}
EXPECT_EQ((unsigned)6, bl.get_num_buffers());
EXPECT_TRUE((bl.length() & ~CEPH_PAGE_MASK) == 0);
EXPECT_FALSE(bl.is_page_aligned());
bl.rebuild_page_aligned();
EXPECT_TRUE(bl.is_page_aligned());
EXPECT_EQ((unsigned)4, bl.get_num_buffers());
}
}
TEST(BufferList, claim_append) {
bufferlist from;
{
bufferptr ptr(2);
from.append(ptr);
}
bufferlist to;
{
bufferptr ptr(4);
to.append(ptr);
}
EXPECT_EQ((unsigned)4, to.length());
EXPECT_EQ((unsigned)1, to.get_num_buffers());
to.claim_append(from);
EXPECT_EQ((unsigned)(4 + 2), to.length());
EXPECT_EQ((unsigned)4, to.front().length());
EXPECT_EQ((unsigned)2, to.back().length());
EXPECT_EQ((unsigned)2, to.get_num_buffers());
EXPECT_EQ((unsigned)0, from.get_num_buffers());
EXPECT_EQ((unsigned)0, from.length());
}
TEST(BufferList, begin) {
bufferlist bl;
bl.append("ABC");
bufferlist::iterator i = bl.begin();
EXPECT_EQ('A', *i);
}
TEST(BufferList, end) {
bufferlist bl;
bl.append("AB");
bufferlist::iterator i = bl.end();
bl.append("C");
EXPECT_EQ('C', bl[i.get_off()]);
}
TEST(BufferList, append) {
//
// void append(char c);
//
{
bufferlist bl;
EXPECT_EQ((unsigned)0, bl.get_num_buffers());
bl.append('A');
EXPECT_EQ((unsigned)1, bl.get_num_buffers());
//EXPECT_TRUE(bl.is_aligned(CEPH_BUFFER_APPEND_SIZE));
}
//
// void append(const char *data, unsigned len);
//
{
bufferlist bl(CEPH_PAGE_SIZE);
std::string str(CEPH_PAGE_SIZE * 2, 'X');
bl.append(str.c_str(), str.size());
EXPECT_EQ((unsigned)2, bl.get_num_buffers());
EXPECT_EQ(CEPH_PAGE_SIZE, bl.front().length());
EXPECT_EQ(CEPH_PAGE_SIZE, bl.back().length());
}
//
// void append(const std::string& s);
//
{
bufferlist bl(CEPH_PAGE_SIZE);
std::string str(CEPH_PAGE_SIZE * 2, 'X');
bl.append(str);
EXPECT_EQ((unsigned)2, bl.get_num_buffers());
EXPECT_EQ(CEPH_PAGE_SIZE, bl.front().length());
EXPECT_EQ(CEPH_PAGE_SIZE, bl.back().length());
}
//
// void append(const ptr& bp);
//
{
bufferlist bl;
EXPECT_EQ((unsigned)0, bl.get_num_buffers());
EXPECT_EQ((unsigned)0, bl.length());
{
bufferptr ptr;
bl.append(ptr);
EXPECT_EQ((unsigned)0, bl.get_num_buffers());
EXPECT_EQ((unsigned)0, bl.length());
}
{
bufferptr ptr(3);
bl.append(ptr);
EXPECT_EQ((unsigned)1, bl.get_num_buffers());
EXPECT_EQ((unsigned)3, bl.length());
}
}
//
// void append(const ptr& bp, unsigned off, unsigned len);
//
{
bufferlist bl;
bl.append('A');
bufferptr back(bl.back());
bufferptr in(back);
EXPECT_EQ((unsigned)1, bl.get_num_buffers());
EXPECT_EQ((unsigned)1, bl.length());
{
PrCtl unset_dumpable;
EXPECT_DEATH(bl.append(in, (unsigned)100, (unsigned)100), "");
}
EXPECT_LT((unsigned)0, in.unused_tail_length());
in.append('B');
bl.append(in, back.end(), 1);
EXPECT_EQ((unsigned)1, bl.get_num_buffers());
EXPECT_EQ((unsigned)2, bl.length());
EXPECT_EQ('B', bl[1]);
}
{
bufferlist bl;
EXPECT_EQ((unsigned)0, bl.get_num_buffers());
EXPECT_EQ((unsigned)0, bl.length());
bufferptr ptr(2);
ptr.set_length(0);
ptr.append("AB", 2);
bl.append(ptr, 1, 1);
EXPECT_EQ((unsigned)1, bl.get_num_buffers());
EXPECT_EQ((unsigned)1, bl.length());
}
//
// void append(const list& bl);
//
{
bufferlist bl;
bl.append('A');
bufferlist other;
other.append('B');
bl.append(other);
EXPECT_EQ((unsigned)2, bl.get_num_buffers());
EXPECT_EQ('B', bl[1]);
}
//
// void append(std::istream& in);
//
{
bufferlist bl;
std::string expected("ABC\nDEF\n");
std::istringstream is("ABC\n\nDEF");
bl.append(is);
EXPECT_EQ(0, ::memcmp(expected.c_str(), bl.c_str(), expected.size()));
EXPECT_EQ(expected.size(), bl.length());
}
//
// void append(ptr&& bp);
//
{
bufferlist bl;
EXPECT_EQ((unsigned)0, bl.get_num_buffers());
EXPECT_EQ((unsigned)0, bl.length());
{
bufferptr ptr;
bl.append(std::move(ptr));
EXPECT_EQ((unsigned)0, bl.get_num_buffers());
EXPECT_EQ((unsigned)0, bl.length());
}
{
bufferptr ptr(3);
bl.append(std::move(ptr));
EXPECT_EQ((unsigned)1, bl.get_num_buffers());
EXPECT_EQ((unsigned)3, bl.length());
EXPECT_FALSE(static_cast<instrumented_bptr&>(ptr).get_raw());
}
}
}
TEST(BufferList, append_hole) {
{
bufferlist bl;
auto filler = bl.append_hole(1);
EXPECT_EQ((unsigned)1, bl.get_num_buffers());
EXPECT_EQ((unsigned)1, bl.length());
bl.append("BC", 2);
EXPECT_EQ((unsigned)1, bl.get_num_buffers());
EXPECT_EQ((unsigned)3, bl.length());
const char a = 'A';
filler.copy_in((unsigned)1, &a);
EXPECT_EQ((unsigned)3, bl.length());
EXPECT_EQ(0, ::memcmp("ABC", bl.c_str(), 3));
}
{
bufferlist bl;
bl.append('A');
EXPECT_EQ((unsigned)1, bl.get_num_buffers());
EXPECT_EQ((unsigned)1, bl.length());
auto filler = bl.append_hole(1);
EXPECT_EQ((unsigned)1, bl.get_num_buffers());
EXPECT_EQ((unsigned)2, bl.length());
bl.append('C');
EXPECT_EQ((unsigned)1, bl.get_num_buffers());
EXPECT_EQ((unsigned)3, bl.length());
const char b = 'B';
filler.copy_in((unsigned)1, &b);
EXPECT_EQ((unsigned)3, bl.length());
EXPECT_EQ(0, ::memcmp("ABC", bl.c_str(), 3));
}
}
TEST(BufferList, append_zero) {
bufferlist bl;
bl.append('A');
EXPECT_EQ((unsigned)1, bl.get_num_buffers());
EXPECT_EQ((unsigned)1, bl.length());
bl.append_zero(1);
EXPECT_EQ((unsigned)1, bl.get_num_buffers());
EXPECT_EQ((unsigned)2, bl.length());
EXPECT_EQ('\0', bl[1]);
}
TEST(BufferList, operator_brackets) {
bufferlist bl;
EXPECT_THROW(bl[1], buffer::end_of_buffer);
bl.append('A');
bufferlist other;
other.append('B');
bl.append(other);
EXPECT_EQ((unsigned)2, bl.get_num_buffers());
EXPECT_EQ('B', bl[1]);
}
TEST(BufferList, c_str) {
bufferlist bl;
EXPECT_EQ((const char*)NULL, bl.c_str());
bl.append('A');
bufferlist other;
other.append('B');
bl.append(other);
EXPECT_EQ((unsigned)2, bl.get_num_buffers());
EXPECT_EQ(0, ::memcmp("AB", bl.c_str(), 2));
}
TEST(BufferList, c_str_carriage) {
// verify the c_str() optimization for carriage handling
buffer::ptr bp("A", 1);
bufferlist bl;
bl.append(bp);
bl.append('B');
EXPECT_EQ(2U, bl.get_num_buffers());
EXPECT_EQ(2U, bl.length());
// this should leave an empty bptr for carriage at the end of the bl
bl.splice(1, 1);
EXPECT_EQ(2U, bl.get_num_buffers());
EXPECT_EQ(1U, bl.length());
std::ignore = bl.c_str();
// if we have an empty bptr at the end, we don't need to rebuild
EXPECT_EQ(2U, bl.get_num_buffers());
}
TEST(BufferList, substr_of) {
bufferlist bl;
EXPECT_THROW(bl.substr_of(bl, 1, 1), buffer::end_of_buffer);
const char *s[] = {
"ABC",
"DEF",
"GHI",
"JKL"
};
for (unsigned i = 0; i < 4; i++) {
bufferptr ptr(s[i], strlen(s[i]));
bl.push_back(ptr);
}
EXPECT_EQ((unsigned)4, bl.get_num_buffers());
bufferlist other;
other.append("TO BE CLEARED");
other.substr_of(bl, 4, 4);
EXPECT_EQ((unsigned)2, other.get_num_buffers());
EXPECT_EQ((unsigned)4, other.length());
EXPECT_EQ(0, ::memcmp("EFGH", other.c_str(), 4));
}
TEST(BufferList, splice) {
bufferlist bl;
EXPECT_THROW(bl.splice(1, 1), buffer::end_of_buffer);
const char *s[] = {
"ABC",
"DEF",
"GHI",
"JKL"
};
for (unsigned i = 0; i < 4; i++) {
bufferptr ptr(s[i], strlen(s[i]));
bl.push_back(ptr);
}
EXPECT_EQ((unsigned)4, bl.get_num_buffers());
bl.splice(0, 0);
bufferlist other;
other.append('X');
bl.splice(4, 4, &other);
EXPECT_EQ((unsigned)3, other.get_num_buffers());
EXPECT_EQ((unsigned)5, other.length());
EXPECT_EQ(0, ::memcmp("XEFGH", other.c_str(), other.length()));
EXPECT_EQ((unsigned)8, bl.length());
{
bufferlist tmp(bl);
EXPECT_EQ(0, ::memcmp("ABCDIJKL", tmp.c_str(), tmp.length()));
}
bl.splice(4, 4);
EXPECT_EQ((unsigned)4, bl.length());
EXPECT_EQ(0, ::memcmp("ABCD", bl.c_str(), bl.length()));
{
bl.clear();
bufferptr ptr1("0123456789", 10);
bl.push_back(ptr1);
bufferptr ptr2("abcdefghij", 10);
bl.append(ptr2, 5, 5);
other.clear();
bl.splice(10, 4, &other);
EXPECT_EQ((unsigned)11, bl.length());
EXPECT_EQ(0, ::memcmp("fghi", other.c_str(), other.length()));
}
}
TEST(BufferList, write) {
std::ostringstream stream;
bufferlist bl;
bl.append("ABC");
bl.write(1, 2, stream);
EXPECT_EQ("BC", stream.str());
}
TEST(BufferList, encode_base64) {
bufferlist bl;
bl.append("ABCD");
bufferlist other;
bl.encode_base64(other);
const char *expected = "QUJDRA==";
EXPECT_EQ(0, ::memcmp(expected, other.c_str(), strlen(expected)));
}
TEST(BufferList, decode_base64) {
bufferlist bl;
bl.append("QUJDRA==");
bufferlist other;
other.decode_base64(bl);
const char *expected = "ABCD";
EXPECT_EQ(0, ::memcmp(expected, other.c_str(), strlen(expected)));
bufferlist malformed;
malformed.append("QUJDRA");
EXPECT_THROW(other.decode_base64(malformed), buffer::malformed_input);
}
TEST(BufferList, hexdump) {
bufferlist bl;
std::ostringstream stream;
bl.append("013245678901234\0006789012345678901234", 32);
bl.hexdump(stream);
EXPECT_EQ("00000000 30 31 33 32 34 35 36 37 38 39 30 31 32 33 34 00 |013245678901234.|\n"
"00000010 36 37 38 39 30 31 32 33 34 35 36 37 38 39 30 31 |6789012345678901|\n"
"00000020\n",
stream.str());
}
TEST(BufferList, read_file) {
std::string error;
bufferlist bl;
::unlink(FILENAME);
EXPECT_EQ(-ENOENT, bl.read_file("UNLIKELY", &error));
snprintf(cmd, sizeof(cmd), "echo ABC> %s", FILENAME);
EXPECT_EQ(0, ::system(cmd));
#ifndef _WIN32
snprintf(cmd, sizeof(cmd), "chmod 0 %s", FILENAME);
EXPECT_EQ(0, ::system(cmd));
if (getuid() != 0) {
EXPECT_EQ(-EACCES, bl.read_file(FILENAME, &error));
}
snprintf(cmd, sizeof(cmd), "chmod +r %s", FILENAME);
EXPECT_EQ(0, ::system(cmd));
#endif /* _WIN32 */
EXPECT_EQ(0, bl.read_file(FILENAME, &error));
::unlink(FILENAME);
EXPECT_EQ((unsigned)4, bl.length());
std::string actual(bl.c_str(), bl.length());
EXPECT_EQ("ABC\n", actual);
}
TEST(BufferList, read_fd) {
unsigned len = 4;
::unlink(FILENAME);
snprintf(cmd, sizeof(cmd), "echo ABC > %s", FILENAME);
EXPECT_EQ(0, ::system(cmd));
int fd = -1;
bufferlist bl;
EXPECT_EQ(-EBADF, bl.read_fd(fd, len));
fd = ::open(FILENAME, O_RDONLY);
ASSERT_NE(-1, fd);
EXPECT_EQ(len, (unsigned)bl.read_fd(fd, len));
//EXPECT_EQ(CEPH_BUFFER_APPEND_SIZE - len, bl.front().unused_tail_length());
EXPECT_EQ(len, bl.length());
::close(fd);
::unlink(FILENAME);
}
TEST(BufferList, write_file) {
::unlink(FILENAME);
int mode = 0600;
bufferlist bl;
EXPECT_EQ(-ENOENT, bl.write_file("un/like/ly", mode));
bl.append("ABC");
EXPECT_EQ(0, bl.write_file(FILENAME, mode));
struct stat st;
memset(&st, 0, sizeof(st));
ASSERT_EQ(0, ::stat(FILENAME, &st));
#ifndef _WIN32
EXPECT_EQ((unsigned)(mode | S_IFREG), st.st_mode);
#endif
::unlink(FILENAME);
}
TEST(BufferList, write_fd) {
::unlink(FILENAME);
int fd = ::open(FILENAME, O_WRONLY|O_CREAT|O_TRUNC, 0600);
ASSERT_NE(-1, fd);
bufferlist bl;
for (unsigned i = 0; i < IOV_MAX * 2; i++) {
bufferptr ptr("A", 1);
bl.push_back(ptr);
}
EXPECT_EQ(0, bl.write_fd(fd));
::close(fd);
struct stat st;
memset(&st, 0, sizeof(st));
ASSERT_EQ(0, ::stat(FILENAME, &st));
EXPECT_EQ(IOV_MAX * 2, st.st_size);
::unlink(FILENAME);
}
TEST(BufferList, write_fd_offset) {
::unlink(FILENAME);
int fd = ::open(FILENAME, O_WRONLY|O_CREAT|O_TRUNC, 0600);
ASSERT_NE(-1, fd);
bufferlist bl;
for (unsigned i = 0; i < IOV_MAX * 2; i++) {
bufferptr ptr("A", 1);
bl.push_back(ptr);
}
uint64_t offset = 200;
EXPECT_EQ(0, bl.write_fd(fd, offset));
::close(fd);
struct stat st;
memset(&st, 0, sizeof(st));
ASSERT_EQ(0, ::stat(FILENAME, &st));
EXPECT_EQ(IOV_MAX * 2 + offset, (unsigned)st.st_size);
::unlink(FILENAME);
}
TEST(BufferList, crc32c) {
bufferlist bl;
__u32 crc = 0;
bl.append("A");
crc = bl.crc32c(crc);
EXPECT_EQ((unsigned)0xB3109EBF, crc);
crc = bl.crc32c(crc);
EXPECT_EQ((unsigned)0x5FA5C0CC, crc);
}
TEST(BufferList, crc32c_append) {
bufferlist bl1;
bufferlist bl2;
for (int j = 0; j < 200; ++j) {
bufferlist bl;
for (int i = 0; i < 200; ++i) {
char x = rand();
bl.append(x);
bl1.append(x);
}
bl.crc32c(rand()); // mess with the cached bufferptr crc values
bl2.append(bl);
}
ASSERT_EQ(bl1.crc32c(0), bl2.crc32c(0));
}
TEST(BufferList, crc32c_zeros) {
char buffer[4*1024];
for (size_t i=0; i < sizeof(buffer); i++)
{
buffer[i] = i;
}
bufferlist bla;
bufferlist blb;
for (size_t j=0; j < 1000; j++)
{
bufferptr a(buffer, sizeof(buffer));
bla.push_back(a);
uint32_t crca = bla.crc32c(111);
blb.push_back(a);
uint32_t crcb = ceph_crc32c(111, (unsigned char*)blb.c_str(), blb.length());
EXPECT_EQ(crca, crcb);
}
}
TEST(BufferList, crc32c_append_perf) {
int len = 256 * 1024 * 1024;
bufferptr a(len);
bufferptr b(len);
bufferptr c(len);
bufferptr d(len);
std::cout << "populating large buffers (a, b=c=d)" << std::endl;
char *pa = a.c_str();
char *pb = b.c_str();
char *pc = c.c_str();
char *pd = c.c_str();
for (int i=0; i<len; i++) {
pa[i] = (i & 0xff) ^ 73;
pb[i] = (i & 0xff) ^ 123;
pc[i] = (i & 0xff) ^ 123;
pd[i] = (i & 0xff) ^ 123;
}
// track usage of cached crcs
buffer::track_cached_crc(true);
[[maybe_unused]] int base_cached = buffer::get_cached_crc();
[[maybe_unused]] int base_cached_adjusted = buffer::get_cached_crc_adjusted();
bufferlist bla;
bla.push_back(a);
bufferlist blb;
blb.push_back(b);
{
utime_t start = ceph_clock_now();
uint32_t r = bla.crc32c(0);
utime_t end = ceph_clock_now();
float rate = (float)len / (float)(1024*1024) / (float)(end - start);
std::cout << "a.crc32c(0) = " << r << " at " << rate << " MB/sec" << std::endl;
ASSERT_EQ(r, 1138817026u);
}
ceph_assert(buffer::get_cached_crc() == 0 + base_cached);
{
utime_t start = ceph_clock_now();
uint32_t r = bla.crc32c(0);
utime_t end = ceph_clock_now();
float rate = (float)len / (float)(1024*1024) / (float)(end - start);
std::cout << "a.crc32c(0) (again) = " << r << " at " << rate << " MB/sec" << std::endl;
ASSERT_EQ(r, 1138817026u);
}
ceph_assert(buffer::get_cached_crc() == 1 + base_cached);
{
utime_t start = ceph_clock_now();
uint32_t r = bla.crc32c(5);
utime_t end = ceph_clock_now();
float rate = (float)len / (float)(1024*1024) / (float)(end - start);
std::cout << "a.crc32c(5) = " << r << " at " << rate << " MB/sec" << std::endl;
ASSERT_EQ(r, 3239494520u);
}
ceph_assert(buffer::get_cached_crc() == 1 + base_cached);
ceph_assert(buffer::get_cached_crc_adjusted() == 1 + base_cached_adjusted);
{
utime_t start = ceph_clock_now();
uint32_t r = bla.crc32c(5);
utime_t end = ceph_clock_now();
float rate = (float)len / (float)(1024*1024) / (float)(end - start);
std::cout << "a.crc32c(5) (again) = " << r << " at " << rate << " MB/sec" << std::endl;
ASSERT_EQ(r, 3239494520u);
}
ceph_assert(buffer::get_cached_crc() == 1 + base_cached);
ceph_assert(buffer::get_cached_crc_adjusted() == 2 + base_cached_adjusted);
{
utime_t start = ceph_clock_now();
uint32_t r = blb.crc32c(0);
utime_t end = ceph_clock_now();
float rate = (float)len / (float)(1024*1024) / (float)(end - start);
std::cout << "b.crc32c(0) = " << r << " at " << rate << " MB/sec" << std::endl;
ASSERT_EQ(r, 2481791210u);
}
ceph_assert(buffer::get_cached_crc() == 1 + base_cached);
{
utime_t start = ceph_clock_now();
uint32_t r = blb.crc32c(0);
utime_t end = ceph_clock_now();
float rate = (float)len / (float)(1024*1024) / (float)(end - start);
std::cout << "b.crc32c(0) (again)= " << r << " at " << rate << " MB/sec" << std::endl;
ASSERT_EQ(r, 2481791210u);
}
ceph_assert(buffer::get_cached_crc() == 2 + base_cached);
bufferlist ab;
ab.push_back(a);
ab.push_back(b);
{
utime_t start = ceph_clock_now();
uint32_t r = ab.crc32c(0);
utime_t end = ceph_clock_now();
float rate = (float)ab.length() / (float)(1024*1024) / (float)(end - start);
std::cout << "ab.crc32c(0) = " << r << " at " << rate << " MB/sec" << std::endl;
ASSERT_EQ(r, 2988268779u);
}
ceph_assert(buffer::get_cached_crc() == 3 + base_cached);
ceph_assert(buffer::get_cached_crc_adjusted() == 3 + base_cached_adjusted);
bufferlist ac;
ac.push_back(a);
ac.push_back(c);
{
utime_t start = ceph_clock_now();
uint32_t r = ac.crc32c(0);
utime_t end = ceph_clock_now();
float rate = (float)ac.length() / (float)(1024*1024) / (float)(end - start);
std::cout << "ac.crc32c(0) = " << r << " at " << rate << " MB/sec" << std::endl;
ASSERT_EQ(r, 2988268779u);
}
ceph_assert(buffer::get_cached_crc() == 4 + base_cached);
ceph_assert(buffer::get_cached_crc_adjusted() == 3 + base_cached_adjusted);
bufferlist ba;
ba.push_back(b);
ba.push_back(a);
{
utime_t start = ceph_clock_now();
uint32_t r = ba.crc32c(0);
utime_t end = ceph_clock_now();
float rate = (float)ba.length() / (float)(1024*1024) / (float)(end - start);
std::cout << "ba.crc32c(0) = " << r << " at " << rate << " MB/sec" << std::endl;
ASSERT_EQ(r, 169240695u);
}
ceph_assert(buffer::get_cached_crc() == 5 + base_cached);
ceph_assert(buffer::get_cached_crc_adjusted() == 4 + base_cached_adjusted);
{
utime_t start = ceph_clock_now();
uint32_t r = ba.crc32c(5);
utime_t end = ceph_clock_now();
float rate = (float)ba.length() / (float)(1024*1024) / (float)(end - start);
std::cout << "ba.crc32c(5) = " << r << " at " << rate << " MB/sec" << std::endl;
ASSERT_EQ(r, 1265464778u);
}
ceph_assert(buffer::get_cached_crc() == 5 + base_cached);
ceph_assert(buffer::get_cached_crc_adjusted() == 6 + base_cached_adjusted);
cout << "crc cache hits (same start) = " << buffer::get_cached_crc() << std::endl;
cout << "crc cache hits (adjusted) = " << buffer::get_cached_crc_adjusted() << std::endl;
}
TEST(BufferList, compare) {
bufferlist a;
a.append("A");
bufferlist ab; // AB in segments
ab.append(bufferptr("A", 1));
ab.append(bufferptr("B", 1));
bufferlist ac;
ac.append("AC");
//
// bool operator>(bufferlist& l, bufferlist& r)
//
ASSERT_FALSE(a > ab);
ASSERT_TRUE(ab > a);
ASSERT_TRUE(ac > ab);
ASSERT_FALSE(ab > ac);
ASSERT_FALSE(ab > ab);
//
// bool operator>=(bufferlist& l, bufferlist& r)
//
ASSERT_FALSE(a >= ab);
ASSERT_TRUE(ab >= a);
ASSERT_TRUE(ac >= ab);
ASSERT_FALSE(ab >= ac);
ASSERT_TRUE(ab >= ab);
//
// bool operator<(bufferlist& l, bufferlist& r)
//
ASSERT_TRUE(a < ab);
ASSERT_FALSE(ab < a);
ASSERT_FALSE(ac < ab);
ASSERT_TRUE(ab < ac);
ASSERT_FALSE(ab < ab);
//
// bool operator<=(bufferlist& l, bufferlist& r)
//
ASSERT_TRUE(a <= ab);
ASSERT_FALSE(ab <= a);
ASSERT_FALSE(ac <= ab);
ASSERT_TRUE(ab <= ac);
ASSERT_TRUE(ab <= ab);
//
// bool operator==(bufferlist &l, bufferlist &r)
//
ASSERT_FALSE(a == ab);
ASSERT_FALSE(ac == ab);
ASSERT_TRUE(ab == ab);
}
TEST(BufferList, ostream) {
std::ostringstream stream;
bufferlist bl;
const char *s[] = {
"ABC",
"DEF"
};
for (unsigned i = 0; i < 2; i++) {
bufferptr ptr(s[i], strlen(s[i]));
bl.push_back(ptr);
}
stream << bl;
std::cerr << stream.str() << std::endl;
EXPECT_GT(stream.str().size(), stream.str().find("list(len=6,"));
EXPECT_GT(stream.str().size(), stream.str().find("len 3 nref 1),\n"));
EXPECT_GT(stream.str().size(), stream.str().find("len 3 nref 1)\n"));
}
TEST(BufferList, zero) {
//
// void zero()
//
{
bufferlist bl;
bl.append('A');
EXPECT_EQ('A', bl[0]);
bl.zero();
EXPECT_EQ('\0', bl[0]);
}
//
// void zero(unsigned o, unsigned l)
//
const char *s[] = {
"ABC",
"DEF",
"GHI",
"KLM"
};
{
bufferlist bl;
bufferptr ptr(s[0], strlen(s[0]));
bl.push_back(ptr);
bl.zero((unsigned)0, (unsigned)1);
EXPECT_EQ(0, ::memcmp("\0BC", bl.c_str(), 3));
}
{
bufferlist bl;
for (unsigned i = 0; i < 4; i++) {
bufferptr ptr(s[i], strlen(s[i]));
bl.push_back(ptr);
}
{
PrCtl unset_dumpable;
EXPECT_DEATH(bl.zero((unsigned)0, (unsigned)2000), "");
}
bl.zero((unsigned)2, (unsigned)5);
EXPECT_EQ(0, ::memcmp("AB\0\0\0\0\0HIKLM", bl.c_str(), 9));
}
{
bufferlist bl;
for (unsigned i = 0; i < 4; i++) {
bufferptr ptr(s[i], strlen(s[i]));
bl.push_back(ptr);
}
bl.zero((unsigned)3, (unsigned)3);
EXPECT_EQ(0, ::memcmp("ABC\0\0\0GHIKLM", bl.c_str(), 9));
}
{
bufferlist bl;
bufferptr ptr1(4);
bufferptr ptr2(4);
memset(ptr1.c_str(), 'a', 4);
memset(ptr2.c_str(), 'b', 4);
bl.append(ptr1);
bl.append(ptr2);
bl.zero((unsigned)2, (unsigned)4);
EXPECT_EQ(0, ::memcmp("aa\0\0\0\0bb", bl.c_str(), 8));
}
}
TEST(BufferList, EmptyAppend) {
bufferlist bl;
bufferptr ptr;
bl.push_back(ptr);
ASSERT_EQ(bl.begin().end(), 1);
}
TEST(BufferList, InternalCarriage) {
ceph::bufferlist bl;
EXPECT_EQ(bl.get_num_buffers(), 0u);
encode(int64_t(42), bl);
EXPECT_EQ(bl.get_num_buffers(), 1u);
{
ceph::bufferlist bl_with_foo;
bl_with_foo.append("foo", 3);
EXPECT_EQ(bl_with_foo.length(), 3u);
EXPECT_EQ(bl_with_foo.get_num_buffers(), 1u);
bl.append(bl_with_foo);
EXPECT_EQ(bl.get_num_buffers(), 2u);
}
encode(int64_t(24), bl);
EXPECT_EQ(bl.get_num_buffers(), 3u);
}
TEST(BufferList, ContiguousAppender) {
ceph::bufferlist bl;
EXPECT_EQ(bl.get_num_buffers(), 0u);
// we expect a flush in ~contiguous_appender
{
auto ap = bl.get_contiguous_appender(100);
denc(int64_t(42), ap);
EXPECT_EQ(bl.get_num_buffers(), 1u);
// append bufferlist with single ptr inside. This should
// commit changes to bl::_len and the underlying bp::len.
{
ceph::bufferlist bl_with_foo;
bl_with_foo.append("foo", 3);
EXPECT_EQ(bl_with_foo.length(), 3u);
EXPECT_EQ(bl_with_foo.get_num_buffers(), 1u);
ap.append(bl_with_foo);
// 3 as the ap::append(const bl&) splits the bp with free
// space.
EXPECT_EQ(bl.get_num_buffers(), 3u);
}
denc(int64_t(24), ap);
EXPECT_EQ(bl.get_num_buffers(), 3u);
EXPECT_EQ(bl.length(), sizeof(int64_t) + 3u);
}
EXPECT_EQ(bl.length(), 2u * sizeof(int64_t) + 3u);
}
TEST(BufferList, TestPtrAppend) {
bufferlist bl;
char correct[MAX_TEST];
int curpos = 0;
int length = random() % 5 > 0 ? random() % 1000 : 0;
while (curpos + length < MAX_TEST) {
if (!length) {
bufferptr ptr;
bl.push_back(ptr);
} else {
char *current = correct + curpos;
for (int i = 0; i < length; ++i) {
char next = random() % 255;
correct[curpos++] = next;
}
bufferptr ptr(current, length);
bl.append(ptr);
}
length = random() % 5 > 0 ? random() % 1000 : 0;
}
ASSERT_EQ(memcmp(bl.c_str(), correct, curpos), 0);
}
TEST(BufferList, TestDirectAppend) {
bufferlist bl;
char correct[MAX_TEST];
int curpos = 0;
int length = random() % 5 > 0 ? random() % 1000 : 0;
while (curpos + length < MAX_TEST) {
char *current = correct + curpos;
for (int i = 0; i < length; ++i) {
char next = random() % 255;
correct[curpos++] = next;
}
bl.append(current, length);
length = random() % 5 > 0 ? random() % 1000 : 0;
}
ASSERT_EQ(memcmp(bl.c_str(), correct, curpos), 0);
}
TEST(BufferList, TestCopyAll) {
const static size_t BIG_SZ = 10737414;
std::shared_ptr <unsigned char> big(
(unsigned char*)malloc(BIG_SZ), free);
unsigned char c = 0;
for (size_t i = 0; i < BIG_SZ; ++i) {
big.get()[i] = c++;
}
bufferlist bl;
bl.append((const char*)big.get(), BIG_SZ);
bufferlist::iterator i = bl.begin();
bufferlist bl2;
i.copy_all(bl2);
ASSERT_EQ(bl2.length(), BIG_SZ);
std::shared_ptr <unsigned char> big2(
(unsigned char*)malloc(BIG_SZ), free);
bl2.begin().copy(BIG_SZ, (char*)big2.get());
ASSERT_EQ(memcmp(big.get(), big2.get(), BIG_SZ), 0);
}
TEST(BufferList, InvalidateCrc) {
const static size_t buffer_size = 262144;
std::shared_ptr <unsigned char> big(
(unsigned char*)malloc(buffer_size), free);
unsigned char c = 0;
char* ptr = (char*) big.get();
char* inptr;
for (size_t i = 0; i < buffer_size; ++i) {
ptr[i] = c++;
}
bufferlist bl;
// test for crashes (shouldn't crash)
bl.invalidate_crc();
// put data into bufferlist
bl.append((const char*)big.get(), buffer_size);
// get its crc
__u32 crc = bl.crc32c(0);
// modify data in bl without its knowledge
inptr = (char*) bl.c_str();
c = 0;
for (size_t i = 0; i < buffer_size; ++i) {
inptr[i] = c--;
}
// make sure data in bl are now different than in big
EXPECT_NE(memcmp((void*) ptr, (void*) inptr, buffer_size), 0);
// crc should remain the same
__u32 new_crc = bl.crc32c(0);
EXPECT_EQ(crc, new_crc);
// force crc invalidate, check if it is updated
bl.invalidate_crc();
EXPECT_NE(crc, bl.crc32c(0));
}
TEST(BufferList, TestIsProvidedBuffer) {
char buff[100];
bufferlist bl;
bl.push_back(buffer::create_static(100, buff));
ASSERT_TRUE(bl.is_provided_buffer(buff));
bl.append_zero(100);
ASSERT_FALSE(bl.is_provided_buffer(buff));
}
TEST(BufferList, DanglingLastP) {
bufferlist bl;
{
// previously we're using the unsharable buffer type to distinguish
// the last_p-specific problem from the generic crosstalk issues we
// had since the very beginning:
// https://gist.github.com/rzarzynski/aed18372e88aed392101adac3bd87bbc
// this is no longer possible as `buffer::create_unsharable()` has
// been dropped.
bufferptr bp(buffer::create(10));
bp.copy_in(0, 3, "XXX");
bl.push_back(std::move(bp));
EXPECT_EQ(0, ::memcmp("XXX", bl.c_str(), 3));
// let `copy_in` to set `last_p` member of bufferlist
bl.begin().copy_in(2, "AB");
EXPECT_EQ(0, ::memcmp("ABX", bl.c_str(), 3));
}
bufferlist empty;
// before the fix this would have left `last_p` unchanged leading to
// the dangerous dangling state keep in mind that the initial,
// unsharable bptr will be freed.
bl = const_cast<const bufferlist&>(empty);
bl.append("123");
// we must continue from where the previous copy_in had finished.
// Otherwise `bl::copy_in` will call `seek()` and refresh `last_p`.
bl.begin(2).copy_in(1, "C");
EXPECT_EQ(0, ::memcmp("12C", bl.c_str(), 3));
}
TEST(BufferList, size) {
buffer::list bl;
EXPECT_EQ(bl.size(), bl.length());
bl.append("12345");
EXPECT_EQ(bl.size(), bl.length());
bl.clear();
EXPECT_EQ(bl.size(), bl.length());
}
TEST(BufferHash, all) {
{
bufferlist bl;
bl.append("A");
bufferhash hash;
EXPECT_EQ((unsigned)0, hash.digest());
hash.update(bl);
EXPECT_EQ((unsigned)0xB3109EBF, hash.digest());
hash.update(bl);
EXPECT_EQ((unsigned)0x5FA5C0CC, hash.digest());
}
{
bufferlist bl;
bl.append("A");
bufferhash hash;
EXPECT_EQ((unsigned)0, hash.digest());
bufferhash& returned_hash = hash << bl;
EXPECT_EQ(&returned_hash, &hash);
EXPECT_EQ((unsigned)0xB3109EBF, hash.digest());
}
}
/*
* Local Variables:
* compile-command: "cd .. ; make unittest_bufferlist &&
* ulimit -s unlimited ; valgrind \
* --max-stackframe=20000000 --tool=memcheck \
* ./unittest_bufferlist # --gtest_filter=BufferList.constructors"
* End:
*/