Skip to content

Commit c24eb6f

Browse files
committed
iobuf support reserve_aligned
1 parent 0625945 commit c24eb6f

3 files changed

Lines changed: 158 additions & 0 deletions

File tree

src/butil/iobuf.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,47 @@ IOBuf::Block* share_tls_block() {
438438
return new_block;
439439
}
440440

441+
// Get a memory aligned block from TLS. if TLS is not found, create one.
442+
IOBuf::Block* share_tls_aligned_block(size_t alignment) {
443+
auto find_and_align_block = [](IOBuf::Block* const b, size_t alignment) {
444+
if (b->full()) {
445+
return false;
446+
}
447+
const size_t remainder =
448+
reinterpret_cast<uintptr_t>(b->data + b->size) % alignment;
449+
size_t adder = 0;
450+
if (remainder != 0) {
451+
adder = alignment - remainder;
452+
}
453+
size_t left_space = 0;
454+
if (b->left_space() > adder) {
455+
left_space = b->left_space() - adder;
456+
}
457+
if (left_space < alignment) {
458+
return false;
459+
}
460+
b->size += adder; // align block offset
461+
return true;
462+
};
463+
TLSData& tls_data = g_tls_data;
464+
IOBuf::Block* b = tls_data.block_head;
465+
while (b) {
466+
IOBuf::Block* const saved_next = b->u.portal_next;
467+
if (find_and_align_block(b, alignment)) {
468+
return b;
469+
}
470+
b = saved_next;
471+
}
472+
if (!b) {
473+
const size_t align_size = std::max(alignment, 4096UL);
474+
b = create_block(align_size * 3); // make sure it is align satisfied
475+
if (b) {
476+
find_and_align_block(b, alignment); // always return true
477+
}
478+
}
479+
return b;
480+
}
481+
441482
// Return one block to TLS.
442483
inline void release_tls_block(IOBuf::Block* b) {
443484
if (!b) {
@@ -1371,6 +1412,35 @@ IOBuf::Area IOBuf::reserve(size_t count) {
13711412
return result;
13721413
}
13731414

1415+
IOBuf::Area IOBuf::reserve_aligned(size_t n, size_t alignment) {
1416+
bool is_power_two = alignment > 0 && (alignment & (alignment - 1));
1417+
if (is_power_two != 0) {
1418+
LOG(ERROR) << "Invalid alignment, must power of two";
1419+
return INVALID_AREA;
1420+
}
1421+
size_t count = (n + alignment - 1) & ~(alignment - 1);
1422+
IOBuf::Area result = INVALID_AREA;
1423+
size_t total_nc = 0;
1424+
while (total_nc < count) { // excluded count == 0
1425+
IOBuf::Block* b = iobuf::share_tls_aligned_block(alignment);
1426+
if (BAIDU_UNLIKELY(!b)) {
1427+
return INVALID_AREA;
1428+
}
1429+
const size_t nc =
1430+
std::min(count - total_nc, b->left_space()) & ~(alignment - 1);
1431+
const IOBuf::BlockRef r = {(uint32_t)b->size, (uint32_t)nc, b};
1432+
_push_back_ref(r);
1433+
if (total_nc == 0) {
1434+
// Encode the area at first time. Notice that the pushed ref may
1435+
// be merged with existing ones.
1436+
result = make_area(_ref_num() - 1, _back_ref().length - nc, count);
1437+
}
1438+
total_nc += nc;
1439+
b->size += nc;
1440+
}
1441+
return result;
1442+
}
1443+
13741444
int IOBuf::unsafe_assign(Area area, const void* data) {
13751445
if (area == INVALID_AREA || data == NULL) {
13761446
LOG(ERROR) << "Invalid parameters";

src/butil/iobuf.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,11 @@ friend class IOBufCutter;
273273
// NOTE: reserve(0) returns INVALID_AREA.
274274
Area reserve(size_t n);
275275

276+
// Reserve `n' aligned uninitialized bytes at back-side.
277+
// Returns an object representing the reserved area, INVALID_AREA on failure.
278+
// NOTE: reserve(0) returns INVALID_AREA, alignment must be a power of 2.
279+
Area reserve_aligned(size_t n, size_t alignment);
280+
276281
// [EXTREMELY UNSAFE]
277282
// Copy `data' to the reserved `area'. `data' must be as long as the
278283
// reserved size.

test/iobuf_unittest.cpp

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1785,4 +1785,87 @@ TEST_F(IOBufTest, acquire_tls_block) {
17851785
ASSERT_NE(butil::iobuf::block_cap(b), butil::iobuf::block_size(b));
17861786
}
17871787

1788+
TEST_F(IOBufTest, reserve_aligned) {
1789+
{
1790+
butil::IOBuf buf;
1791+
auto area = buf.reserve_aligned(1024, 16);
1792+
ASSERT_NE(area, butil::IOBuf::INVALID_AREA);
1793+
butil::IOBufAsZeroCopyInputStream wrapper(buf);
1794+
const void* data;
1795+
int size;
1796+
int total_size = 0;
1797+
while (wrapper.Next(&data, &size)) {
1798+
ASSERT_EQ(reinterpret_cast<uintptr_t>(data) % 16, 0);
1799+
ASSERT_EQ(size % 16, 0);
1800+
total_size += size;
1801+
}
1802+
ASSERT_EQ(total_size, 1024);
1803+
}
1804+
{
1805+
butil::IOBuf buf;
1806+
auto area = buf.reserve_aligned(1024, 4096);
1807+
ASSERT_NE(area, butil::IOBuf::INVALID_AREA);
1808+
butil::IOBufAsZeroCopyInputStream wrapper(buf);
1809+
const void* data;
1810+
int size;
1811+
int total_size = 0;
1812+
while (wrapper.Next(&data, &size)) {
1813+
ASSERT_EQ(reinterpret_cast<uintptr_t>(data) % 4096, 0);
1814+
ASSERT_EQ(size % 4096, 0);
1815+
total_size += size;
1816+
}
1817+
ASSERT_EQ(total_size, 4096);
1818+
}
1819+
{
1820+
butil::IOBuf buf;
1821+
auto area = buf.reserve_aligned(8191, 4096);
1822+
ASSERT_NE(area, butil::IOBuf::INVALID_AREA);
1823+
butil::IOBufAsZeroCopyInputStream wrapper(buf);
1824+
const void* data;
1825+
int size;
1826+
int total_size = 0;
1827+
while (wrapper.Next(&data, &size)) {
1828+
ASSERT_EQ(reinterpret_cast<uintptr_t>(data) % 4096, 0);
1829+
ASSERT_EQ(size % 4096, 0);
1830+
total_size += size;
1831+
}
1832+
ASSERT_EQ(total_size, 8192);
1833+
}
1834+
{
1835+
butil::IOBuf buf;
1836+
auto area = buf.reserve_aligned(4096 * 10 - 1, 4096);
1837+
ASSERT_NE(area, butil::IOBuf::INVALID_AREA);
1838+
butil::IOBufAsZeroCopyInputStream wrapper(buf);
1839+
const void* data;
1840+
int size;
1841+
int total_size = 0;
1842+
while (wrapper.Next(&data, &size)) {
1843+
ASSERT_EQ(reinterpret_cast<uintptr_t>(data) % 4096, 0);
1844+
ASSERT_EQ(size % 4096, 0);
1845+
total_size += size;
1846+
}
1847+
ASSERT_EQ(total_size, 4096 * 10);
1848+
}
1849+
{
1850+
butil::IOBuf buf;
1851+
auto area = buf.reserve_aligned(4096, 4095);
1852+
ASSERT_EQ(area, butil::IOBuf::INVALID_AREA);
1853+
}
1854+
{
1855+
butil::IOBuf buf;
1856+
auto area = buf.reserve_aligned(4096 * 10 + 1, 8192);
1857+
ASSERT_NE(area, butil::IOBuf::INVALID_AREA);
1858+
butil::IOBufAsZeroCopyInputStream wrapper(buf);
1859+
const void* data;
1860+
int size;
1861+
int total_size = 0;
1862+
while (wrapper.Next(&data, &size)) {
1863+
ASSERT_EQ(reinterpret_cast<uintptr_t>(data) % 4096, 0);
1864+
ASSERT_EQ(size % 4096, 0);
1865+
total_size += size;
1866+
}
1867+
ASSERT_EQ(total_size, 4096 * 10 + 8192);
1868+
}
1869+
}
1870+
17881871
} // namespace

0 commit comments

Comments
 (0)