Skip to content

Commit 23b31f7

Browse files
committed
iobuf support reserve_aligned
1 parent 9855697 commit 23b31f7

3 files changed

Lines changed: 162 additions & 0 deletions

File tree

src/butil/iobuf.cpp

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,51 @@ 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+
// find one block if align satisfied, then aligned the block offset
444+
auto find_and_align_block = [](IOBuf::Block* const b, size_t alignment) {
445+
const size_t remainder =
446+
reinterpret_cast<uintptr_t>(b->data + b->size) % alignment;
447+
size_t adder = 0;
448+
if (remainder != 0) {
449+
adder = alignment - remainder;
450+
}
451+
size_t left_space = 0;
452+
if (b->left_space() > adder) {
453+
left_space = b->left_space() - adder;
454+
}
455+
if (left_space < alignment) {
456+
return false;
457+
}
458+
b->size += adder; // align block offset
459+
return true;
460+
};
461+
TLSData& tls_data = g_tls_data;
462+
IOBuf::Block* b = tls_data.block_head;
463+
while (b && b->full()) {
464+
IOBuf::Block* const saved_next = b->u.portal_next;
465+
b->dec_ref();
466+
tls_data.block_head = saved_next;
467+
--tls_data.num_blocks;
468+
b = saved_next;
469+
}
470+
while (b) {
471+
IOBuf::Block* const saved_next = b->u.portal_next;
472+
if (find_and_align_block(b, alignment)) {
473+
return b;
474+
}
475+
b = saved_next;
476+
}
477+
if (!b) {
478+
b = create_block(alignment * 2 + sizeof(IOBuf::Block)); // may be NULL
479+
if (b) {
480+
find_and_align_block(b, alignment); // always return true
481+
}
482+
}
483+
return b;
484+
}
485+
441486
// Return one block to TLS.
442487
inline void release_tls_block(IOBuf::Block* b) {
443488
if (!b) {
@@ -1371,6 +1416,35 @@ IOBuf::Area IOBuf::reserve(size_t count) {
13711416
return result;
13721417
}
13731418

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