Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
B
brpc
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
submodule
brpc
Commits
37209275
Commit
37209275
authored
7 years ago
by
gejun
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
add missing test/iobuf.proto test/iobuf_unittest.cpp
parent
83affd3c
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
1581 additions
and
0 deletions
+1581
-0
iobuf.proto
test/iobuf.proto
+27
-0
iobuf_unittest.cpp
test/iobuf_unittest.cpp
+1554
-0
No files found.
test/iobuf.proto
0 → 100644
View file @
37209275
package
proto
;
enum
CompressType
{
CompressTypeNone
=
0
;
CompressTypeGzip
=
1
;
CompressTypeZlib
=
2
;
CompressTypeSnappy
=
3
;
CompressTypeLZ4
=
4
;
}
message
Misc
{
required
CompressType
required_enum
=
1
;
optional
CompressType
optional_enum
=
2
;
repeated
CompressType
repeated_enum
=
3
;
required
uint64
required_uint64
=
4
;
optional
uint64
optional_uint64
=
5
;
repeated
uint64
repeated_uint64
=
6
;
required
string
required_string
=
7
;
optional
string
optional_string
=
8
;
repeated
string
repeated_string
=
9
;
required
bool
required_bool
=
10
;
optional
bool
optional_bool
=
11
;
repeated
bool
repeated_bool
=
12
;
required
int32
required_int32
=
13
;
optional
int32
optional_int32
=
14
;
repeated
int32
repeated_int32
=
15
;
}
This diff is collapsed.
Click to expand it.
test/iobuf_unittest.cpp
0 → 100644
View file @
37209275
// Copyright (c) 2010 baidu-rpc authors.
// Author: Ge,Jun (gejun@baidu.com)
// Date: 2010-12-04 11:59
#include <gtest/gtest.h>
#include <sys/types.h>
#include <sys/socket.h> // socketpair
#include <errno.h> // errno
#include <fcntl.h> // O_RDONLY
#include <base/files/temp_file.h> // TempFile
#include <base/containers/flat_map.h>
#include <base/macros.h>
#include <base/time.h> // Timer
#include <base/fd_utility.h> // make_non_blocking
#include <base/iobuf.h>
#include <base/logging.h>
#include <base/fd_guard.h>
#include <base/errno.h>
#include <base/fast_rand.h>
#include "iobuf.pb.h"
namespace
base
{
namespace
iobuf
{
extern
void
*
(
*
blockmem_allocate
)(
size_t
);
extern
void
(
*
blockmem_deallocate
)(
void
*
);
extern
void
reset_blockmem_allocate_and_deallocate
();
extern
int32_t
block_shared_count
(
base
::
IOBuf
::
Block
const
*
b
);
extern
uint32_t
block_cap
(
base
::
IOBuf
::
Block
const
*
b
);
extern
IOBuf
::
Block
*
get_tls_block_head
();
extern
int
get_tls_block_count
();
extern
void
remove_tls_block_chain
();
IOBuf
::
Block
*
get_portal_next
(
IOBuf
::
Block
const
*
b
);
}
}
namespace
{
void
check_tls_block
()
{
ASSERT_EQ
((
base
::
IOBuf
::
Block
*
)
NULL
,
base
::
iobuf
::
get_tls_block_head
());
printf
(
"tls_block of base::IOBuf was deleted
\n
"
);
}
const
int
ALLOW_UNUSED
check_dummy
=
base
::
thread_atexit
(
check_tls_block
);
static
base
::
FlatSet
<
void
*>
s_set
;
void
*
debug_block_allocate
(
size_t
block_size
)
{
void
*
b
=
operator
new
(
block_size
,
std
::
nothrow
);
s_set
.
insert
(
b
);
return
b
;
}
void
debug_block_deallocate
(
void
*
b
)
{
if
(
1ul
!=
s_set
.
erase
(
b
))
{
ASSERT_TRUE
(
false
)
<<
"Bad block="
<<
b
;
}
else
{
operator
delete
(
b
);
}
}
inline
bool
is_debug_allocator_enabled
()
{
return
(
base
::
iobuf
::
blockmem_allocate
==
debug_block_allocate
);
}
void
install_debug_allocator
()
{
if
(
!
is_debug_allocator_enabled
())
{
base
::
iobuf
::
remove_tls_block_chain
();
s_set
.
init
(
1024
);
base
::
iobuf
::
blockmem_allocate
=
debug_block_allocate
;
base
::
iobuf
::
blockmem_deallocate
=
debug_block_deallocate
;
LOG
(
INFO
)
<<
"<Installed debug create/destroy>"
;
}
}
void
show_prof_and_rm
(
const
char
*
bin_name
,
const
char
*
filename
,
size_t
topn
)
{
char
cmd
[
1024
];
if
(
topn
!=
0
)
{
snprintf
(
cmd
,
sizeof
(
cmd
),
"if [ -e %s ] ; then CPUPROFILE_FREQUENCY=1000 ./pprof --text %s %s | head -%lu; rm -f %s; fi"
,
filename
,
bin_name
,
filename
,
topn
+
1
,
filename
);
}
else
{
snprintf
(
cmd
,
sizeof
(
cmd
),
"if [ -e %s ] ; then CPUPROFILE_FREQUENCY=1000 ./pprof --text %s %s; rm -f %s; fi"
,
filename
,
bin_name
,
filename
,
filename
);
}
system
(
cmd
);
}
static
void
check_memory_leak
()
{
if
(
is_debug_allocator_enabled
())
{
base
::
IOBuf
::
Block
*
p
=
base
::
iobuf
::
get_tls_block_head
();
size_t
n
=
0
;
while
(
p
)
{
ASSERT_TRUE
(
s_set
.
seek
(
p
))
<<
"Memory leak: "
<<
p
;
p
=
base
::
iobuf
::
get_portal_next
(
p
);
++
n
;
}
ASSERT_EQ
(
n
,
s_set
.
size
());
ASSERT_EQ
(
n
,
base
::
iobuf
::
get_tls_block_count
());
}
}
class
IOBufTest
:
public
::
testing
::
Test
{
protected
:
IOBufTest
(){};
virtual
~
IOBufTest
(){};
virtual
void
SetUp
()
{
};
virtual
void
TearDown
()
{
check_memory_leak
();
};
};
std
::
string
to_str
(
const
base
::
IOBuf
&
p
)
{
return
p
.
to_string
();
}
TEST_F
(
IOBufTest
,
append_zero
)
{
int
fds
[
2
];
ASSERT_EQ
(
0
,
pipe
(
fds
));
base
::
IOPortal
p
;
ASSERT_EQ
(
0
,
p
.
append_from_file_descriptor
(
fds
[
0
],
0
));
ASSERT_EQ
(
0
,
close
(
fds
[
0
]));
ASSERT_EQ
(
0
,
close
(
fds
[
1
]));
}
TEST_F
(
IOBufTest
,
pop_front
)
{
install_debug_allocator
();
base
::
IOBuf
buf
;
ASSERT_EQ
(
0UL
,
buf
.
pop_front
(
1
));
// nothing happened
std
::
string
s
=
"hello"
;
buf
.
append
(
s
);
ASSERT_EQ
(
s
,
to_str
(
buf
));
ASSERT_EQ
(
0UL
,
buf
.
pop_front
(
0
));
// nothing happened
ASSERT_EQ
(
s
,
to_str
(
buf
));
ASSERT_EQ
(
1UL
,
buf
.
pop_front
(
1
));
s
.
erase
(
0
,
1
);
ASSERT_EQ
(
s
,
to_str
(
buf
));
ASSERT_EQ
(
s
.
length
(),
buf
.
length
());
ASSERT_FALSE
(
buf
.
empty
());
ASSERT_EQ
(
s
.
length
(),
buf
.
pop_front
(
INT_MAX
));
s
.
clear
();
ASSERT_EQ
(
s
,
to_str
(
buf
));
ASSERT_EQ
(
0UL
,
buf
.
length
());
ASSERT_TRUE
(
buf
.
empty
());
for
(
size_t
i
=
0
;
i
<
base
::
IOBuf
::
DEFAULT_PAYLOAD
*
3
/
2
;
++
i
)
{
s
.
push_back
(
i
);
}
buf
.
append
(
s
);
ASSERT_EQ
(
1UL
,
buf
.
pop_front
(
1
));
s
.
erase
(
0
,
1
);
ASSERT_EQ
(
s
,
to_str
(
buf
));
ASSERT_EQ
(
s
.
length
(),
buf
.
length
());
ASSERT_FALSE
(
buf
.
empty
());
ASSERT_EQ
(
s
.
length
(),
buf
.
pop_front
(
INT_MAX
));
s
.
clear
();
ASSERT_EQ
(
s
,
to_str
(
buf
));
ASSERT_EQ
(
0UL
,
buf
.
length
());
ASSERT_TRUE
(
buf
.
empty
());
}
TEST_F
(
IOBufTest
,
pop_back
)
{
install_debug_allocator
();
base
::
IOBuf
buf
;
ASSERT_EQ
(
0UL
,
buf
.
pop_back
(
1
));
// nothing happened
std
::
string
s
=
"hello"
;
buf
.
append
(
s
);
ASSERT_EQ
(
s
,
to_str
(
buf
));
ASSERT_EQ
(
0UL
,
buf
.
pop_back
(
0
));
// nothing happened
ASSERT_EQ
(
s
,
to_str
(
buf
));
ASSERT_EQ
(
1UL
,
buf
.
pop_back
(
1
));
s
.
resize
(
s
.
size
()
-
1
);
ASSERT_EQ
(
s
,
to_str
(
buf
));
ASSERT_EQ
(
s
.
length
(),
buf
.
length
());
ASSERT_FALSE
(
buf
.
empty
());
ASSERT_EQ
(
s
.
length
(),
buf
.
pop_back
(
INT_MAX
));
s
.
clear
();
ASSERT_EQ
(
s
,
to_str
(
buf
));
ASSERT_EQ
(
0UL
,
buf
.
length
());
ASSERT_TRUE
(
buf
.
empty
());
for
(
size_t
i
=
0
;
i
<
base
::
IOBuf
::
DEFAULT_PAYLOAD
*
3
/
2
;
++
i
)
{
s
.
push_back
(
i
);
}
buf
.
append
(
s
);
ASSERT_EQ
(
1UL
,
buf
.
pop_back
(
1
));
s
.
resize
(
s
.
size
()
-
1
);
ASSERT_EQ
(
s
,
to_str
(
buf
));
ASSERT_EQ
(
s
.
length
(),
buf
.
length
());
ASSERT_FALSE
(
buf
.
empty
());
ASSERT_EQ
(
s
.
length
(),
buf
.
pop_back
(
INT_MAX
));
s
.
clear
();
ASSERT_EQ
(
s
,
to_str
(
buf
));
ASSERT_EQ
(
0UL
,
buf
.
length
());
ASSERT_TRUE
(
buf
.
empty
());
}
TEST_F
(
IOBufTest
,
append
)
{
install_debug_allocator
();
base
::
IOBuf
b
;
ASSERT_EQ
(
0UL
,
b
.
length
());
ASSERT_TRUE
(
b
.
empty
());
ASSERT_EQ
(
-
1
,
b
.
append
(
NULL
));
ASSERT_EQ
(
0
,
b
.
append
(
""
));
ASSERT_EQ
(
0
,
b
.
append
(
std
::
string
()));
ASSERT_EQ
(
-
1
,
b
.
append
(
NULL
,
1
));
ASSERT_EQ
(
0
,
b
.
append
(
"dummy"
,
0
));
ASSERT_EQ
(
0UL
,
b
.
length
());
ASSERT_TRUE
(
b
.
empty
());
ASSERT_EQ
(
0
,
b
.
append
(
"1"
));
ASSERT_EQ
(
1UL
,
b
.
length
());
ASSERT_FALSE
(
b
.
empty
());
ASSERT_EQ
(
"1"
,
to_str
(
b
));
const
std
::
string
s
=
"22"
;
ASSERT_EQ
(
0
,
b
.
append
(
s
));
ASSERT_EQ
(
3UL
,
b
.
length
());
ASSERT_FALSE
(
b
.
empty
());
ASSERT_EQ
(
"122"
,
to_str
(
b
));
}
TEST_F
(
IOBufTest
,
appendv
)
{
install_debug_allocator
();
base
::
IOBuf
b
;
const_iovec
vec
[]
=
{
{
"hello1"
,
6
},
{
" world1"
,
7
},
{
"hello2"
,
6
},
{
" world2"
,
7
},
{
"hello3"
,
6
},
{
" world3"
,
7
},
{
"hello4"
,
6
},
{
" world4"
,
7
},
{
"hello5"
,
6
},
{
" world5"
,
7
}
};
ASSERT_EQ
(
0
,
b
.
appendv
(
vec
,
arraysize
(
vec
)));
ASSERT_EQ
(
"hello1 world1hello2 world2hello3 world3hello4 world4hello5 world5"
,
b
.
to_string
());
// Make some iov_len shorter to test if iov_len works.
vec
[
2
].
iov_len
=
4
;
// "hello2"
vec
[
5
].
iov_len
=
3
;
// " world3"
b
.
clear
();
ASSERT_EQ
(
0
,
b
.
appendv
(
vec
,
arraysize
(
vec
)));
ASSERT_EQ
(
"hello1 world1hell world2hello3 wohello4 world4hello5 world5"
,
b
.
to_string
());
// Append some long stuff.
const
size_t
full_len
=
base
::
IOBuf
::
DEFAULT_PAYLOAD
*
9
;
char
*
str
=
(
char
*
)
malloc
(
full_len
);
ASSERT_TRUE
(
str
);
const
size_t
len1
=
full_len
/
6
;
const
size_t
len2
=
full_len
/
3
;
const
size_t
len3
=
full_len
-
len1
-
len2
;
ASSERT_GT
(
len1
,
(
size_t
)
base
::
IOBuf
::
DEFAULT_PAYLOAD
);
ASSERT_GT
(
len2
,
(
size_t
)
base
::
IOBuf
::
DEFAULT_PAYLOAD
);
ASSERT_GT
(
len3
,
(
size_t
)
base
::
IOBuf
::
DEFAULT_PAYLOAD
);
ASSERT_EQ
(
full_len
,
len1
+
len2
+
len3
);
for
(
size_t
i
=
0
;
i
<
full_len
;
++
i
)
{
str
[
i
]
=
i
*
7
;
}
const_iovec
vec2
[]
=
{{
str
,
len1
},
{
str
+
len1
,
len2
},
{
str
+
len1
+
len2
,
len3
}};
b
.
clear
();
ASSERT_EQ
(
0
,
b
.
appendv
(
vec2
,
arraysize
(
vec2
)));
ASSERT_EQ
(
full_len
,
b
.
size
());
ASSERT_EQ
(
0
,
memcmp
(
str
,
b
.
to_string
().
data
(),
full_len
));
}
TEST_F
(
IOBufTest
,
reserve
)
{
base
::
IOBuf
b
;
ASSERT_EQ
(
base
::
IOBuf
::
INVALID_AREA
,
b
.
reserve
(
0
));
const
size_t
NRESERVED1
=
5
;
const
base
::
IOBuf
::
Area
a1
=
b
.
reserve
(
NRESERVED1
);
ASSERT_TRUE
(
a1
!=
base
::
IOBuf
::
INVALID_AREA
);
ASSERT_EQ
(
NRESERVED1
,
b
.
size
());
b
.
append
(
"hello world"
);
ASSERT_EQ
(
0
,
b
.
unsafe_assign
(
a1
,
"prefix"
));
// `x' will not be copied
ASSERT_EQ
(
"prefihello world"
,
b
.
to_string
());
ASSERT_EQ
(
16
,
b
.
size
());
// pop/append sth. from back-side and assign again.
ASSERT_EQ
(
5
,
b
.
pop_back
(
5
));
ASSERT_EQ
(
"prefihello "
,
b
.
to_string
());
b
.
append
(
"blahblahfoobar"
);
ASSERT_EQ
(
0
,
b
.
unsafe_assign
(
a1
,
"goodorbad"
));
// `x' will not be copied
ASSERT_EQ
(
"goodohello blahblahfoobar"
,
b
.
to_string
());
// append a long string and assign again.
std
::
string
s1
(
base
::
IOBuf
::
DEFAULT_PAYLOAD
*
3
,
'\0'
);
for
(
size_t
i
=
0
;
i
<
s1
.
size
();
++
i
)
{
s1
[
i
]
=
i
*
7
;
}
ASSERT_EQ
(
base
::
IOBuf
::
DEFAULT_PAYLOAD
*
3
,
s1
.
size
());
// remove everything after reserved area
ASSERT_GE
(
b
.
size
(),
NRESERVED1
);
b
.
pop_back
(
b
.
size
()
-
NRESERVED1
);
ASSERT_EQ
(
NRESERVED1
,
b
.
size
());
b
.
append
(
s1
);
ASSERT_EQ
(
0
,
b
.
unsafe_assign
(
a1
,
"appleblahblah"
));
ASSERT_EQ
(
"apple"
+
s1
,
b
.
to_string
());
// Reserve long
b
.
pop_back
(
b
.
size
()
-
NRESERVED1
);
ASSERT_EQ
(
NRESERVED1
,
b
.
size
());
const
size_t
NRESERVED2
=
base
::
IOBuf
::
DEFAULT_PAYLOAD
*
3
;
const
base
::
IOBuf
::
Area
a2
=
b
.
reserve
(
NRESERVED2
);
ASSERT_EQ
(
NRESERVED1
+
NRESERVED2
,
b
.
size
());
b
.
append
(
s1
);
ASSERT_EQ
(
NRESERVED1
+
NRESERVED2
+
s1
.
size
(),
b
.
size
());
std
::
string
s2
(
NRESERVED2
,
0
);
for
(
size_t
i
=
0
;
i
<
s2
.
size
();
++
i
)
{
s2
[
i
]
=
i
*
13
;
}
ASSERT_EQ
(
NRESERVED2
,
s2
.
size
());
ASSERT_EQ
(
0
,
b
.
unsafe_assign
(
a2
,
s2
.
data
()));
ASSERT_EQ
(
"apple"
+
s2
+
s1
,
b
.
to_string
());
ASSERT_EQ
(
0
,
b
.
unsafe_assign
(
a1
,
"orangeblahblah"
));
ASSERT_EQ
(
"orang"
+
s2
+
s1
,
b
.
to_string
());
}
struct
FakeBlock
{
int
nshared
;
FakeBlock
()
:
nshared
(
1
)
{}
};
TEST_F
(
IOBufTest
,
iobuf_as_queue
)
{
install_debug_allocator
();
// If INITIAL_CAP gets bigger, creating base::IOBuf::Block are very
// small. Since We don't access base::IOBuf::Block::data in this case.
// We replace base::IOBuf::Block with FakeBlock with only nshared (in
// the same offset)
FakeBlock
*
blocks
[
base
::
IOBuf
::
INITIAL_CAP
+
16
];
const
size_t
NBLOCKS
=
ARRAY_SIZE
(
blocks
);
base
::
IOBuf
::
BlockRef
r
[
NBLOCKS
];
const
size_t
LENGTH
=
7UL
;
for
(
size_t
i
=
0
;
i
<
NBLOCKS
;
++
i
)
{
ASSERT_TRUE
((
blocks
[
i
]
=
new
FakeBlock
));
r
[
i
].
offset
=
1
;
r
[
i
].
length
=
LENGTH
;
r
[
i
].
block
=
(
base
::
IOBuf
::
Block
*
)
blocks
[
i
];
}
base
::
IOBuf
p
;
// Empty
ASSERT_EQ
(
0UL
,
p
.
_ref_num
());
ASSERT_EQ
(
-
1
,
p
.
_pop_front_ref
());
ASSERT_EQ
(
0UL
,
p
.
length
());
// Add one ref
p
.
_push_back_ref
(
r
[
0
]);
ASSERT_EQ
(
1UL
,
p
.
_ref_num
());
ASSERT_EQ
(
LENGTH
,
p
.
length
());
ASSERT_EQ
(
r
[
0
],
p
.
_front_ref
());
ASSERT_EQ
(
r
[
0
],
p
.
_back_ref
());
ASSERT_EQ
(
r
[
0
],
p
.
_ref_at
(
0
));
ASSERT_EQ
(
2
,
base
::
iobuf
::
block_shared_count
(
r
[
0
].
block
));
// Add second ref
p
.
_push_back_ref
(
r
[
1
]);
ASSERT_EQ
(
2UL
,
p
.
_ref_num
());
ASSERT_EQ
(
LENGTH
*
2
,
p
.
length
());
ASSERT_EQ
(
r
[
0
],
p
.
_front_ref
());
ASSERT_EQ
(
r
[
1
],
p
.
_back_ref
());
ASSERT_EQ
(
r
[
0
],
p
.
_ref_at
(
0
));
ASSERT_EQ
(
r
[
1
],
p
.
_ref_at
(
1
));
ASSERT_EQ
(
2
,
base
::
iobuf
::
block_shared_count
(
r
[
1
].
block
));
// Pop a ref
ASSERT_EQ
(
0
,
p
.
_pop_front_ref
());
ASSERT_EQ
(
1UL
,
p
.
_ref_num
());
ASSERT_EQ
(
LENGTH
,
p
.
length
());
ASSERT_EQ
(
r
[
1
],
p
.
_front_ref
());
ASSERT_EQ
(
r
[
1
],
p
.
_back_ref
());
ASSERT_EQ
(
r
[
1
],
p
.
_ref_at
(
0
));
//ASSERT_EQ(1, base::iobuf::block_shared_count(r[0].block));
// Pop second
ASSERT_EQ
(
0
,
p
.
_pop_front_ref
());
ASSERT_EQ
(
0UL
,
p
.
_ref_num
());
ASSERT_EQ
(
0UL
,
p
.
length
());
//ASSERT_EQ(1, r[1].block->nshared);
// Add INITIAL_CAP+2 refs, r[0] and r[1] are used, don't use again
for
(
size_t
i
=
0
;
i
<
base
::
IOBuf
::
INITIAL_CAP
+
2
;
++
i
)
{
p
.
_push_back_ref
(
r
[
i
+
2
]);
ASSERT_EQ
(
i
+
1
,
p
.
_ref_num
());
ASSERT_EQ
(
p
.
_ref_num
()
*
LENGTH
,
p
.
length
());
ASSERT_EQ
(
r
[
2
],
p
.
_front_ref
())
<<
i
;
ASSERT_EQ
(
r
[
i
+
2
],
p
.
_back_ref
());
for
(
size_t
j
=
0
;
j
<=
i
;
j
+=
std
::
max
(
1UL
,
i
/
20
)
/*not check all*/
)
{
ASSERT_EQ
(
r
[
j
+
2
],
p
.
_ref_at
(
j
));
}
ASSERT_EQ
(
2
,
base
::
iobuf
::
block_shared_count
(
r
[
i
+
2
].
block
));
}
// Pop them all
const
size_t
saved_ref_num
=
p
.
_ref_num
();
while
(
p
.
_ref_num
()
>=
2UL
)
{
const
size_t
last_ref_num
=
p
.
_ref_num
();
ASSERT_EQ
(
0
,
p
.
_pop_front_ref
());
ASSERT_EQ
(
last_ref_num
,
p
.
_ref_num
()
+
1
);
ASSERT_EQ
(
p
.
_ref_num
()
*
LENGTH
,
p
.
length
());
const
size_t
f
=
saved_ref_num
-
p
.
_ref_num
()
+
2
;
ASSERT_EQ
(
r
[
f
],
p
.
_front_ref
());
ASSERT_EQ
(
r
[
saved_ref_num
+
1
],
p
.
_back_ref
());
for
(
size_t
j
=
0
;
j
<
p
.
_ref_num
();
j
+=
std
::
max
(
1UL
,
p
.
_ref_num
()
/
20
))
{
ASSERT_EQ
(
r
[
j
+
f
],
p
.
_ref_at
(
j
));
}
//ASSERT_EQ(1, r[f-1].block->nshared);
}
ASSERT_EQ
(
1ul
,
p
.
_ref_num
());
// Pop last one
ASSERT_EQ
(
0
,
p
.
_pop_front_ref
());
ASSERT_EQ
(
0UL
,
p
.
_ref_num
());
ASSERT_EQ
(
0UL
,
p
.
length
());
//ASSERT_EQ(1, r[saved_ref_num+1].block->nshared);
// Delete blocks
for
(
size_t
i
=
0
;
i
<
NBLOCKS
;
++
i
)
{
delete
blocks
[
i
];
}
}
TEST_F
(
IOBufTest
,
iobuf_sanity
)
{
install_debug_allocator
();
LOG
(
INFO
)
<<
"sizeof(base::IOBuf)="
<<
sizeof
(
base
::
IOBuf
)
<<
" sizeof(IOPortal)="
<<
sizeof
(
base
::
IOPortal
);
base
::
IOBuf
b1
;
std
::
string
s1
=
"hello world"
;
const
char
c1
=
'A'
;
const
std
::
string
s2
=
"too simple"
;
std
::
string
s1c
=
s1
;
s1c
.
erase
(
0
,
1
);
// Append a c-std::string
ASSERT_EQ
(
0
,
b1
.
append
(
s1
.
c_str
()));
ASSERT_EQ
(
s1
.
length
(),
b1
.
length
());
ASSERT_EQ
(
s1
,
to_str
(
b1
));
ASSERT_EQ
(
1UL
,
b1
.
_ref_num
());
// Append a char
ASSERT_EQ
(
0
,
b1
.
push_back
(
c1
));
ASSERT_EQ
(
s1
.
length
()
+
1
,
b1
.
length
());
ASSERT_EQ
(
s1
+
c1
,
to_str
(
b1
));
ASSERT_EQ
(
1UL
,
b1
.
_ref_num
());
// Append a std::string
ASSERT_EQ
(
0
,
b1
.
append
(
s2
));
ASSERT_EQ
(
s1
.
length
()
+
1
+
s2
.
length
(),
b1
.
length
());
ASSERT_EQ
(
s1
+
c1
+
s2
,
to_str
(
b1
));
ASSERT_EQ
(
1UL
,
b1
.
_ref_num
());
// Pop first char
ASSERT_EQ
(
1UL
,
b1
.
pop_front
(
1
));
ASSERT_EQ
(
1UL
,
b1
.
_ref_num
());
ASSERT_EQ
(
s1
.
length
()
+
s2
.
length
(),
b1
.
length
());
ASSERT_EQ
(
s1c
+
c1
+
s2
,
to_str
(
b1
));
// Pop all
ASSERT_EQ
(
0UL
,
b1
.
pop_front
(
0
));
ASSERT_EQ
(
s1
.
length
()
+
s2
.
length
(),
b1
.
pop_front
(
b1
.
length
()
+
1
));
ASSERT_TRUE
(
b1
.
empty
());
ASSERT_EQ
(
0UL
,
b1
.
length
());
ASSERT_EQ
(
0UL
,
b1
.
_ref_num
());
ASSERT_EQ
(
""
,
to_str
(
b1
));
// Restore
ASSERT_EQ
(
0
,
b1
.
append
(
s1
.
c_str
()));
ASSERT_EQ
(
0
,
b1
.
push_back
(
c1
));
ASSERT_EQ
(
0
,
b1
.
append
(
s2
));
// Cut first char
base
::
IOBuf
p
;
b1
.
cutn
(
&
p
,
0
);
b1
.
cutn
(
&
p
,
1
);
ASSERT_EQ
(
s1
.
substr
(
0
,
1
),
to_str
(
p
));
ASSERT_EQ
(
s1c
+
c1
+
s2
,
to_str
(
b1
));
// Cut another two and append to p
b1
.
cutn
(
&
p
,
2
);
ASSERT_EQ
(
s1
.
substr
(
0
,
3
),
to_str
(
p
));
std
::
string
s1d
=
s1
;
s1d
.
erase
(
0
,
3
);
ASSERT_EQ
(
s1d
+
c1
+
s2
,
to_str
(
b1
));
// Clear
b1
.
clear
();
ASSERT_TRUE
(
b1
.
empty
());
ASSERT_EQ
(
0UL
,
b1
.
length
());
ASSERT_EQ
(
0UL
,
b1
.
_ref_num
());
ASSERT_EQ
(
""
,
to_str
(
b1
));
ASSERT_EQ
(
s1
.
substr
(
0
,
3
),
to_str
(
p
));
}
TEST_F
(
IOBufTest
,
copy_and_assign
)
{
install_debug_allocator
();
const
size_t
TARGET_SIZE
=
base
::
IOBuf
::
BLOCK_SIZE
*
2
;
base
::
IOBuf
buf0
;
buf0
.
append
(
"hello"
);
ASSERT_EQ
(
1u
,
buf0
.
_ref_num
());
// Copy-construct from SmallView
base
::
IOBuf
buf1
=
buf0
;
ASSERT_EQ
(
1u
,
buf1
.
_ref_num
());
ASSERT_EQ
(
buf0
,
buf1
);
buf1
.
resize
(
TARGET_SIZE
,
'z'
);
ASSERT_LT
(
2u
,
buf1
.
_ref_num
());
ASSERT_EQ
(
TARGET_SIZE
,
buf1
.
size
());
// Copy-construct from BigView
base
::
IOBuf
buf2
=
buf1
;
ASSERT_EQ
(
buf1
,
buf2
);
// assign BigView to SmallView
base
::
IOBuf
buf3
;
buf3
=
buf1
;
ASSERT_EQ
(
buf1
,
buf3
);
// assign BigView to BigView
base
::
IOBuf
buf4
;
buf4
.
resize
(
TARGET_SIZE
,
'w'
);
ASSERT_NE
(
buf1
,
buf4
);
buf4
=
buf1
;
ASSERT_EQ
(
buf1
,
buf4
);
}
TEST_F
(
IOBufTest
,
compare
)
{
install_debug_allocator
();
const
char
*
SEED
=
"abcdefghijklmnqopqrstuvwxyz"
;
base
::
IOBuf
seedbuf
;
seedbuf
.
append
(
SEED
);
const
int
REP
=
100
;
base
::
IOBuf
b1
;
for
(
int
i
=
0
;
i
<
REP
;
++
i
)
{
b1
.
append
(
seedbuf
);
b1
.
append
(
SEED
);
}
base
::
IOBuf
b2
;
for
(
int
i
=
0
;
i
<
REP
*
2
;
++
i
)
{
b2
.
append
(
SEED
);
}
ASSERT_EQ
(
b1
,
b2
);
base
::
IOBuf
b3
=
b2
;
b2
.
push_back
(
'0'
);
ASSERT_NE
(
b1
,
b2
);
ASSERT_EQ
(
b1
,
b3
);
b1
.
clear
();
b2
.
clear
();
ASSERT_EQ
(
b1
,
b2
);
}
TEST_F
(
IOBufTest
,
append_and_cut_it_all
)
{
base
::
IOBuf
b
;
const
size_t
N
=
32768UL
;
for
(
size_t
i
=
0
;
i
<
N
;
++
i
)
{
ASSERT_EQ
(
0
,
b
.
push_back
(
i
));
}
ASSERT_EQ
(
N
,
b
.
length
());
base
::
IOBuf
p
;
b
.
cutn
(
&
p
,
N
);
ASSERT_TRUE
(
b
.
empty
());
ASSERT_EQ
(
N
,
p
.
length
());
}
TEST_F
(
IOBufTest
,
copy_to
)
{
base
::
IOBuf
b
;
const
std
::
string
seed
=
"abcdefghijklmnopqrstuvwxyz"
;
std
::
string
src
;
for
(
size_t
i
=
0
;
i
<
1000
;
++
i
)
{
src
.
append
(
seed
);
}
b
.
append
(
src
);
ASSERT_GT
(
b
.
size
(),
base
::
IOBuf
::
DEFAULT_PAYLOAD
);
std
::
string
s1
;
ASSERT_EQ
(
src
.
size
(),
b
.
copy_to
(
&
s1
));
ASSERT_EQ
(
src
,
s1
);
std
::
string
s2
;
ASSERT_EQ
(
32u
,
b
.
copy_to
(
&
s2
,
32
));
ASSERT_EQ
(
src
.
substr
(
0
,
32
),
s2
);
std
::
string
s3
;
const
std
::
string
expected
=
src
.
substr
(
base
::
IOBuf
::
DEFAULT_PAYLOAD
-
1
,
33
);
ASSERT_EQ
(
33u
,
b
.
copy_to
(
&
s3
,
33
,
base
::
IOBuf
::
DEFAULT_PAYLOAD
-
1
));
ASSERT_EQ
(
expected
,
s3
);
ASSERT_EQ
(
33u
,
b
.
append_to
(
&
s3
,
33
,
base
::
IOBuf
::
DEFAULT_PAYLOAD
-
1
));
ASSERT_EQ
(
expected
+
expected
,
s3
);
base
::
IOBuf
b1
;
ASSERT_EQ
(
src
.
size
(),
b
.
append_to
(
&
b1
));
ASSERT_EQ
(
src
,
b1
.
to_string
());
base
::
IOBuf
b2
;
ASSERT_EQ
(
32u
,
b
.
append_to
(
&
b2
,
32
));
ASSERT_EQ
(
src
.
substr
(
0
,
32
),
b2
.
to_string
());
base
::
IOBuf
b3
;
ASSERT_EQ
(
33u
,
b
.
append_to
(
&
b3
,
33
,
base
::
IOBuf
::
DEFAULT_PAYLOAD
-
1
));
ASSERT_EQ
(
expected
,
b3
.
to_string
());
ASSERT_EQ
(
33u
,
b
.
append_to
(
&
b3
,
33
,
base
::
IOBuf
::
DEFAULT_PAYLOAD
-
1
));
ASSERT_EQ
(
expected
+
expected
,
b3
.
to_string
());
}
TEST_F
(
IOBufTest
,
cut_by_single_text_delim
)
{
install_debug_allocator
();
base
::
IOBuf
b
;
base
::
IOBuf
p
;
std
::
vector
<
base
::
IOBuf
>
ps
;
std
::
string
s1
=
"1234567
\n
12
\n\n
2567"
;
ASSERT_EQ
(
0
,
b
.
append
(
s1
));
ASSERT_EQ
(
s1
.
length
(),
b
.
length
());
for
(;
b
.
cut_until
(
&
p
,
"
\n
"
)
==
0
;
)
{
ps
.
push_back
(
p
);
p
.
clear
();
}
ASSERT_EQ
(
3UL
,
ps
.
size
());
ASSERT_EQ
(
"1234567"
,
to_str
(
ps
[
0
]));
ASSERT_EQ
(
"12"
,
to_str
(
ps
[
1
]));
ASSERT_EQ
(
""
,
to_str
(
ps
[
2
]));
ASSERT_EQ
(
"2567"
,
to_str
(
b
));
b
.
append
(
"
\n
"
);
ASSERT_EQ
(
0
,
b
.
cut_until
(
&
p
,
"
\n
"
));
ASSERT_EQ
(
"2567"
,
to_str
(
p
));
ASSERT_EQ
(
""
,
to_str
(
b
));
}
TEST_F
(
IOBufTest
,
cut_by_multiple_text_delim
)
{
install_debug_allocator
();
base
::
IOBuf
b
;
base
::
IOBuf
p
;
std
::
vector
<
base
::
IOBuf
>
ps
;
std
::
string
s1
=
"
\r\n
1234567
\r\n
12
\r\n\n\r
2567"
;
ASSERT_EQ
(
0
,
b
.
append
(
s1
));
ASSERT_EQ
(
s1
.
length
(),
b
.
length
());
for
(;
b
.
cut_until
(
&
p
,
"
\r\n
"
)
==
0
;
)
{
ps
.
push_back
(
p
);
p
.
clear
();
}
ASSERT_EQ
(
3UL
,
ps
.
size
());
ASSERT_EQ
(
""
,
to_str
(
ps
[
0
]));
ASSERT_EQ
(
"1234567"
,
to_str
(
ps
[
1
]));
ASSERT_EQ
(
"12"
,
to_str
(
ps
[
2
]));
ASSERT_EQ
(
"
\n\r
2567"
,
to_str
(
b
));
b
.
append
(
"
\r\n
"
);
ASSERT_EQ
(
0
,
b
.
cut_until
(
&
p
,
"
\r\n
"
));
ASSERT_EQ
(
"
\n\r
2567"
,
to_str
(
p
));
ASSERT_EQ
(
""
,
to_str
(
b
));
}
TEST_F
(
IOBufTest
,
append_a_lot_and_cut_them_all
)
{
install_debug_allocator
();
base
::
IOBuf
b
;
base
::
IOBuf
p
;
std
::
string
s1
=
"12345678901234567"
;
const
size_t
N
=
10000
;
for
(
size_t
i
=
0
;
i
<
N
;
++
i
)
{
b
.
append
(
s1
);
}
ASSERT_EQ
(
N
*
s1
.
length
(),
b
.
length
());
while
(
b
.
length
()
>=
7
)
{
b
.
cutn
(
&
p
,
7
);
}
size_t
remain
=
s1
.
length
()
*
N
%
7
;
ASSERT_EQ
(
remain
,
b
.
length
());
ASSERT_EQ
(
s1
.
substr
(
s1
.
length
()
-
remain
,
remain
),
to_str
(
b
));
ASSERT_EQ
(
s1
.
length
()
*
N
/
7
*
7
,
p
.
length
());
}
TEST_F
(
IOBufTest
,
cut_into_fd_tiny
)
{
install_debug_allocator
();
base
::
IOPortal
b1
,
b2
;
std
::
string
ref
;
int
fds
[
2
];
for
(
int
j
=
10
;
j
>
0
;
--
j
)
{
ref
.
push_back
(
j
);
}
b1
.
append
(
ref
);
ASSERT_EQ
(
1UL
,
b1
.
pop_front
(
1
));
ref
.
erase
(
0
,
1
);
ASSERT_EQ
(
ref
,
to_str
(
b1
));
LOG
(
INFO
)
<<
"ref.size="
<<
ref
.
size
();
//ASSERT_EQ(0, pipe(fds));
ASSERT_EQ
(
0
,
socketpair
(
AF_UNIX
,
SOCK_STREAM
,
0
,
fds
));
base
::
make_non_blocking
(
fds
[
0
]);
base
::
make_non_blocking
(
fds
[
1
]);
while
(
!
b1
.
empty
()
||
b2
.
length
()
!=
ref
.
length
())
{
size_t
b1len
=
b1
.
length
(),
b2len
=
b2
.
length
();
errno
=
0
;
printf
(
"b1.length=%lu - %ld (%m), "
,
b1len
,
b1
.
cut_into_file_descriptor
(
fds
[
1
]));
printf
(
"b2.length=%lu + %ld (%m)
\n
"
,
b2len
,
b2
.
append_from_file_descriptor
(
fds
[
0
],
LONG_MAX
));
}
printf
(
"b1.length=%lu, b2.length=%lu
\n
"
,
b1
.
length
(),
b2
.
length
());
ASSERT_EQ
(
ref
,
to_str
(
b2
));
close
(
fds
[
0
]);
close
(
fds
[
1
]);
}
TEST_F
(
IOBufTest
,
cut_multiple_into_fd_tiny
)
{
install_debug_allocator
();
base
::
IOBuf
*
b1
[
10
];
base
::
IOPortal
b2
;
std
::
string
ref
;
int
fds
[
2
];
for
(
size_t
j
=
0
;
j
<
ARRAY_SIZE
(
b1
);
++
j
)
{
std
::
string
s
;
for
(
int
i
=
10
;
i
>
0
;
--
i
)
{
s
.
push_back
(
j
*
10
+
i
);
}
ref
.
append
(
s
);
base
::
IOPortal
*
b
=
new
base
::
IOPortal
();
b
->
append
(
s
);
b1
[
j
]
=
b
;
}
ASSERT_EQ
(
0
,
socketpair
(
AF_UNIX
,
SOCK_STREAM
,
0
,
fds
));
base
::
make_non_blocking
(
fds
[
0
]);
base
::
make_non_blocking
(
fds
[
1
]);
ASSERT_EQ
((
ssize_t
)
ref
.
length
(),
base
::
IOBuf
::
cut_multiple_into_file_descriptor
(
fds
[
1
],
b1
,
ARRAY_SIZE
(
b1
)));
for
(
size_t
j
=
0
;
j
<
ARRAY_SIZE
(
b1
);
++
j
)
{
ASSERT_TRUE
(
b1
[
j
]
->
empty
());
delete
(
base
::
IOPortal
*
)
b1
[
j
];
b1
[
j
]
=
NULL
;
}
ASSERT_EQ
((
ssize_t
)
ref
.
length
(),
b2
.
append_from_file_descriptor
(
fds
[
0
],
LONG_MAX
));
ASSERT_EQ
(
ref
,
to_str
(
b2
));
close
(
fds
[
0
]);
close
(
fds
[
1
]);
}
TEST_F
(
IOBufTest
,
cut_into_fd_a_lot_of_data
)
{
install_debug_allocator
();
base
::
IOPortal
b0
,
b1
,
b2
;
std
::
string
s
,
ref
;
int
fds
[
2
];
for
(
int
j
=
rand
()
%
7777
+
300000
;
j
>
0
;
--
j
)
{
ref
.
push_back
(
j
);
s
.
push_back
(
j
);
if
(
s
.
length
()
==
1024UL
||
j
==
1
)
{
b0
.
append
(
s
);
ref
.
append
(
"tailing"
);
b0
.
cutn
(
&
b1
,
b0
.
length
());
ASSERT_EQ
(
0
,
b1
.
append
(
"tailing"
));
s
.
clear
();
}
}
ASSERT_EQ
(
ref
.
length
(),
b1
.
length
());
ASSERT_EQ
(
ref
,
to_str
(
b1
));
ASSERT_TRUE
(
b0
.
empty
());
LOG
(
INFO
)
<<
"ref.size="
<<
ref
.
size
();
//ASSERT_EQ(0, pipe(fds));
ASSERT_EQ
(
0
,
socketpair
(
AF_UNIX
,
SOCK_STREAM
,
0
,
fds
));
base
::
make_non_blocking
(
fds
[
0
]);
base
::
make_non_blocking
(
fds
[
1
]);
const
int
sockbufsize
=
16
*
1024
-
17
;
ASSERT_EQ
(
0
,
setsockopt
(
fds
[
1
],
SOL_SOCKET
,
SO_SNDBUF
,
(
const
char
*
)
&
sockbufsize
,
sizeof
(
int
)));
ASSERT_EQ
(
0
,
setsockopt
(
fds
[
0
],
SOL_SOCKET
,
SO_RCVBUF
,
(
const
char
*
)
&
sockbufsize
,
sizeof
(
int
)));
while
(
!
b1
.
empty
()
||
b2
.
length
()
!=
ref
.
length
())
{
size_t
b1len
=
b1
.
length
(),
b2len
=
b2
.
length
();
errno
=
0
;
printf
(
"b1.length=%lu - %ld (%m), "
,
b1len
,
b1
.
cut_into_file_descriptor
(
fds
[
1
]));
printf
(
"b2.length=%lu + %ld (%m)
\n
"
,
b2len
,
b2
.
append_from_file_descriptor
(
fds
[
0
],
LONG_MAX
));
}
printf
(
"b1.length=%lu, b2.length=%lu
\n
"
,
b1
.
length
(),
b2
.
length
());
ASSERT_EQ
(
ref
,
to_str
(
b2
));
close
(
fds
[
0
]);
close
(
fds
[
1
]);
}
TEST_F
(
IOBufTest
,
cut_by_delim_perf
)
{
base
::
iobuf
::
reset_blockmem_allocate_and_deallocate
();
base
::
IOBuf
b
;
base
::
IOBuf
p
;
std
::
vector
<
base
::
IOBuf
>
ps
;
std
::
string
s1
=
"123456789012345678901234567890
\n
"
;
const
size_t
N
=
100000
;
for
(
size_t
i
=
0
;
i
<
N
;
++
i
)
{
ASSERT_EQ
(
0
,
b
.
append
(
s1
));
}
ASSERT_EQ
(
N
*
s1
.
length
(),
b
.
length
());
base
::
Timer
t
;
//ProfilerStart("cutd.prof");
t
.
start
();
for
(;
b
.
cut_until
(
&
p
,
"
\n
"
)
==
0
;
)
{
}
t
.
stop
();
//ProfilerStop();
LOG
(
INFO
)
<<
"IOPortal::cut_by_delim takes "
<<
t
.
n_elapsed
()
/
N
<<
"ns, tp="
<<
s1
.
length
()
*
N
*
1000.0
/
t
.
n_elapsed
()
<<
"MB/s"
;
show_prof_and_rm
(
"test_iobuf"
,
"cutd.prof"
,
10
);
}
TEST_F
(
IOBufTest
,
cut_perf
)
{
base
::
iobuf
::
reset_blockmem_allocate_and_deallocate
();
base
::
IOBuf
b
;
base
::
IOBuf
p
;
const
size_t
length
=
60000000UL
;
const
size_t
REP
=
10
;
base
::
Timer
t
;
std
::
string
s
=
"1234567890"
;
const
bool
push_char
=
false
;
if
(
!
push_char
)
{
//ProfilerStart("iobuf_append.prof");
t
.
start
();
for
(
size_t
j
=
0
;
j
<
REP
;
++
j
)
{
b
.
clear
();
for
(
size_t
i
=
0
;
i
<
length
/
s
.
length
();
++
i
)
{
b
.
append
(
s
);
}
}
t
.
stop
();
//ProfilerStop();
LOG
(
INFO
)
<<
"IOPortal::append(std::string_"
<<
s
.
length
()
<<
") takes "
<<
t
.
n_elapsed
()
*
s
.
length
()
/
length
/
REP
<<
"ns, tp="
<<
REP
*
length
*
1000.0
/
t
.
n_elapsed
()
<<
"MB/s"
;
}
else
{
//ProfilerStart("iobuf_pushback.prof");
t
.
start
();
for
(
size_t
i
=
0
;
i
<
length
;
++
i
)
{
b
.
push_back
(
i
);
}
t
.
stop
();
//ProfilerStop();
LOG
(
INFO
)
<<
"IOPortal::push_back(char) takes "
<<
t
.
n_elapsed
()
/
length
<<
"ns, tp="
<<
length
*
1000.0
/
t
.
n_elapsed
()
<<
"MB/s"
;
}
ASSERT_EQ
(
length
,
b
.
length
());
size_t
w
[
3
]
=
{
16
,
128
,
1024
};
//char name[32];
for
(
size_t
i
=
0
;
i
<
ARRAY_SIZE
(
w
);
++
i
)
{
// snprintf(name, sizeof(name), "iobuf_cut%lu.prof", w[i]);
// ProfilerStart(name);
t
.
start
();
while
(
b
.
length
()
>=
w
[
i
]
+
12
)
{
b
.
cutn
(
&
p
,
12
);
b
.
cutn
(
&
p
,
w
[
i
]);
}
t
.
stop
();
//ProfilerStop();
LOG
(
INFO
)
<<
"IOPortal::cutn(12+"
<<
w
[
i
]
<<
") takes "
<<
t
.
n_elapsed
()
*
(
w
[
i
]
+
12
)
/
length
<<
"ns, tp="
<<
length
*
1000.0
/
t
.
n_elapsed
()
<<
"MB/s"
;
ASSERT_LT
(
b
.
length
(),
w
[
i
]
+
12
);
t
.
start
();
b
.
append
(
p
);
t
.
stop
();
LOG
(
INFO
)
<<
"IOPortal::append(base::IOBuf) takes "
<<
t
.
n_elapsed
()
/
p
.
_ref_num
()
<<
"ns, tp="
<<
length
*
1000.0
/
t
.
n_elapsed
()
<<
"MB/s"
;
p
.
clear
();
ASSERT_EQ
(
length
,
b
.
length
());
}
show_prof_and_rm
(
"test_iobuf"
,
"./iobuf_append.prof"
,
10
);
show_prof_and_rm
(
"test_iobuf"
,
"./iobuf_pushback.prof"
,
10
);
}
TEST_F
(
IOBufTest
,
append_store_append_cut
)
{
base
::
iobuf
::
reset_blockmem_allocate_and_deallocate
();
std
::
string
ref
;
ref
.
resize
(
rand
()
%
376813
+
19777777
);
for
(
size_t
j
=
0
;
j
<
ref
.
size
();
++
j
)
{
ref
[
j
]
=
j
;
}
base
::
IOPortal
b1
,
b2
;
std
::
vector
<
base
::
IOBuf
>
ps
;
ssize_t
nr
;
size_t
HINT
=
16
*
1024UL
;
base
::
Timer
t
;
size_t
w
[
3
]
=
{
16
,
128
,
1024
};
char
name
[
64
];
char
profname
[
64
];
char
cmd
[
512
];
bool
write_to_dev_null
=
true
;
size_t
nappend
,
ncut
;
base
::
TempFile
f
;
ASSERT_EQ
(
0
,
f
.
save_bin
(
ref
.
data
(),
ref
.
length
()));
for
(
size_t
i
=
0
;
i
<
ARRAY_SIZE
(
w
);
++
i
)
{
ps
.
reserve
(
ref
.
size
()
/
(
w
[
i
]
+
12
)
+
1
);
// LOG(INFO) << "ps.cap=" << ps.capacity();
const
int
ifd
=
open
(
f
.
fname
(),
O_RDONLY
);
ASSERT_TRUE
(
ifd
>
0
);
if
(
write_to_dev_null
)
{
snprintf
(
name
,
sizeof
(
name
),
"/dev/null"
);
}
else
{
snprintf
(
name
,
sizeof
(
name
),
"iobuf_asac%lu.output"
,
w
[
i
]);
snprintf
(
cmd
,
sizeof
(
cmd
),
"cmp %s %s"
,
f
.
fname
(),
name
);
}
const
int
ofd
=
open
(
name
,
O_CREAT
|
O_WRONLY
,
0666
);
ASSERT_TRUE
(
ofd
>
0
)
<<
strerror
(
errno
);
snprintf
(
profname
,
sizeof
(
profname
),
"iobuf_asac%lu.prof"
,
w
[
i
]);
//ProfilerStart(profname);
t
.
start
();
nappend
=
ncut
=
0
;
while
((
nr
=
b1
.
append_from_file_descriptor
(
ifd
,
HINT
))
>
0
)
{
++
nappend
;
while
(
b1
.
length
()
>=
w
[
i
]
+
12
)
{
base
::
IOBuf
p
;
b1
.
cutn
(
&
p
,
12
);
b1
.
cutn
(
&
p
,
w
[
i
]);
ps
.
push_back
(
p
);
}
}
for
(
size_t
j
=
0
;
j
<
ps
.
size
();
++
j
)
{
b2
.
append
(
ps
[
j
]);
if
(
b2
.
length
()
>=
HINT
)
{
b2
.
cut_into_file_descriptor
(
ofd
);
}
}
b2
.
cut_into_file_descriptor
(
ofd
);
b1
.
cut_into_file_descriptor
(
ofd
);
close
(
ifd
);
close
(
ofd
);
t
.
stop
();
//ProfilerStop();
ASSERT_TRUE
(
b1
.
empty
());
ASSERT_TRUE
(
b2
.
empty
());
//LOG(INFO) << "ps.size=" << ps.size();
ps
.
clear
();
LOG
(
INFO
)
<<
"Bandwidth of append("
<<
f
.
fname
()
<<
")->cut(12+"
<<
w
[
i
]
<<
")->store->append->cut("
<<
name
<<
") is "
<<
ref
.
length
()
*
1000.0
/
t
.
n_elapsed
()
<<
"MB/s"
;
if
(
!
write_to_dev_null
)
{
ASSERT_EQ
(
0
,
system
(
cmd
));
}
remove
(
name
);
}
for
(
size_t
i
=
0
;
i
<
ARRAY_SIZE
(
w
);
++
i
)
{
snprintf
(
name
,
sizeof
(
name
),
"iobuf_asac%lu.prof"
,
w
[
i
]);
show_prof_and_rm
(
"test_iobuf"
,
name
,
10
);
}
}
TEST_F
(
IOBufTest
,
conversion_with_protobuf
)
{
const
int
REP
=
1000
;
proto
::
Misc
m1
;
m1
.
set_required_enum
(
proto
::
CompressTypeGzip
);
m1
.
set_required_uint64
(
0xdeadbeef
);
m1
.
set_optional_uint64
(
10000
);
m1
.
set_required_string
(
"hello iobuf"
);
m1
.
set_optional_string
(
"hello iobuf again"
);
for
(
int
i
=
0
;
i
<
REP
;
++
i
)
{
char
buf
[
16
];
snprintf
(
buf
,
sizeof
(
buf
),
"value%d"
,
i
);
m1
.
add_repeated_string
(
buf
);
}
m1
.
set_required_bool
(
true
);
m1
.
set_required_int32
(
0xbeefdead
);
base
::
IOBuf
buf
;
const
std
::
string
header
(
"just-make-sure-wrapper-does-not-clear-IOBuf"
);
ASSERT_EQ
(
0
,
buf
.
append
(
header
));
base
::
IOBufAsZeroCopyOutputStream
out_wrapper
(
&
buf
);
ASSERT_EQ
(
0
,
out_wrapper
.
ByteCount
());
ASSERT_TRUE
(
m1
.
SerializeToZeroCopyStream
(
&
out_wrapper
));
ASSERT_EQ
((
size_t
)
m1
.
ByteSize
()
+
header
.
size
(),
buf
.
length
());
ASSERT_EQ
(
m1
.
ByteSize
(),
out_wrapper
.
ByteCount
());
ASSERT_EQ
(
header
.
size
(),
buf
.
pop_front
(
header
.
size
()));
base
::
IOBufAsZeroCopyInputStream
in_wrapper
(
buf
);
ASSERT_EQ
(
0
,
in_wrapper
.
ByteCount
());
{
const
void
*
dummy_blk
=
NULL
;
int
dummy_size
=
0
;
ASSERT_TRUE
(
in_wrapper
.
Next
(
&
dummy_blk
,
&
dummy_size
));
ASSERT_EQ
(
dummy_size
,
in_wrapper
.
ByteCount
());
in_wrapper
.
BackUp
(
1
);
ASSERT_EQ
(
dummy_size
-
1
,
in_wrapper
.
ByteCount
());
const
void
*
dummy_blk2
=
NULL
;
int
dummy_size2
=
0
;
ASSERT_TRUE
(
in_wrapper
.
Next
(
&
dummy_blk2
,
&
dummy_size2
));
ASSERT_EQ
(
1
,
dummy_size2
);
ASSERT_EQ
((
char
*
)
dummy_blk
+
dummy_size
-
1
,
(
char
*
)
dummy_blk2
);
ASSERT_EQ
(
dummy_size
,
in_wrapper
.
ByteCount
());
in_wrapper
.
BackUp
(
dummy_size
);
ASSERT_EQ
(
0
,
in_wrapper
.
ByteCount
());
}
proto
::
Misc
m2
;
ASSERT_TRUE
(
m2
.
ParseFromZeroCopyStream
(
&
in_wrapper
));
ASSERT_EQ
(
m1
.
ByteSize
(),
in_wrapper
.
ByteCount
());
ASSERT_EQ
(
m2
.
ByteSize
(),
in_wrapper
.
ByteCount
());
ASSERT_EQ
(
m1
.
required_enum
(),
m2
.
required_enum
());
ASSERT_FALSE
(
m2
.
has_optional_enum
());
ASSERT_EQ
(
m1
.
required_uint64
(),
m2
.
required_uint64
());
ASSERT_TRUE
(
m2
.
has_optional_uint64
());
ASSERT_EQ
(
m1
.
optional_uint64
(),
m2
.
optional_uint64
());
ASSERT_EQ
(
m1
.
required_string
(),
m2
.
required_string
());
ASSERT_TRUE
(
m2
.
has_optional_string
());
ASSERT_EQ
(
m1
.
optional_string
(),
m2
.
optional_string
());
ASSERT_EQ
(
REP
,
m2
.
repeated_string_size
());
for
(
int
i
=
0
;
i
<
REP
;
++
i
)
{
ASSERT_EQ
(
m1
.
repeated_string
(
i
),
m2
.
repeated_string
(
i
));
}
ASSERT_EQ
(
m1
.
required_bool
(),
m2
.
required_bool
());
ASSERT_FALSE
(
m2
.
has_optional_bool
());
ASSERT_EQ
(
m1
.
required_int32
(),
m2
.
required_int32
());
ASSERT_FALSE
(
m2
.
has_optional_int32
());
}
TEST_F
(
IOBufTest
,
extended_backup
)
{
for
(
int
i
=
0
;
i
<
2
;
++
i
)
{
std
::
cout
<<
"i="
<<
i
<<
std
::
endl
;
// Consume the left TLS block so that cases are easier to check.
base
::
iobuf
::
remove_tls_block_chain
();
base
::
IOBuf
src
;
const
int
BLKSIZE
=
(
i
==
0
?
1024
:
base
::
IOBuf
::
DEFAULT_BLOCK_SIZE
);
const
int
PLDSIZE
=
BLKSIZE
-
16
;
// impl dependent.
base
::
IOBufAsZeroCopyOutputStream
out_stream1
(
&
src
,
BLKSIZE
);
base
::
IOBufAsZeroCopyOutputStream
out_stream2
(
&
src
);
base
::
IOBufAsZeroCopyOutputStream
&
out_stream
=
(
i
==
0
?
out_stream1
:
out_stream2
);
void
*
blk1
=
NULL
;
int
size1
=
0
;
ASSERT_TRUE
(
out_stream
.
Next
(
&
blk1
,
&
size1
));
ASSERT_EQ
(
PLDSIZE
,
size1
);
ASSERT_EQ
(
size1
,
out_stream
.
ByteCount
());
void
*
blk2
=
NULL
;
int
size2
=
0
;
ASSERT_TRUE
(
out_stream
.
Next
(
&
blk2
,
&
size2
));
ASSERT_EQ
(
PLDSIZE
,
size2
);
ASSERT_EQ
(
size1
+
size2
,
out_stream
.
ByteCount
());
// BackUp a size that's valid for all ZeroCopyOutputStream
out_stream
.
BackUp
(
PLDSIZE
/
2
);
ASSERT_EQ
(
size1
+
size2
-
PLDSIZE
/
2
,
out_stream
.
ByteCount
());
void
*
blk3
=
NULL
;
int
size3
=
0
;
ASSERT_TRUE
(
out_stream
.
Next
(
&
blk3
,
&
size3
));
ASSERT_EQ
((
char
*
)
blk2
+
PLDSIZE
/
2
,
blk3
);
ASSERT_EQ
(
PLDSIZE
/
2
,
size3
);
ASSERT_EQ
(
size1
+
size2
,
out_stream
.
ByteCount
());
// BackUp a size that's undefined in regular ZeroCopyOutputStream
out_stream
.
BackUp
(
PLDSIZE
*
2
);
ASSERT_EQ
(
0
,
out_stream
.
ByteCount
());
void
*
blk4
=
NULL
;
int
size4
=
0
;
ASSERT_TRUE
(
out_stream
.
Next
(
&
blk4
,
&
size4
));
ASSERT_EQ
(
PLDSIZE
,
size4
);
ASSERT_EQ
(
size4
,
out_stream
.
ByteCount
());
if
(
i
==
1
)
{
ASSERT_EQ
(
blk1
,
blk4
);
}
void
*
blk5
=
NULL
;
int
size5
=
0
;
ASSERT_TRUE
(
out_stream
.
Next
(
&
blk5
,
&
size5
));
ASSERT_EQ
(
PLDSIZE
,
size5
);
ASSERT_EQ
(
size4
+
size5
,
out_stream
.
ByteCount
());
if
(
i
==
1
)
{
ASSERT_EQ
(
blk2
,
blk5
);
}
}
}
TEST_F
(
IOBufTest
,
backup_iobuf_never_called_next
)
{
{
// Consume the left TLS block so that later cases are easier
// to check.
base
::
IOBuf
dummy
;
base
::
IOBufAsZeroCopyOutputStream
dummy_stream
(
&
dummy
);
void
*
dummy_data
=
NULL
;
int
dummy_size
=
0
;
ASSERT_TRUE
(
dummy_stream
.
Next
(
&
dummy_data
,
&
dummy_size
));
}
base
::
IOBuf
src
;
const
size_t
N
=
base
::
IOBuf
::
DEFAULT_PAYLOAD
*
2
;
src
.
resize
(
N
);
ASSERT_EQ
(
2u
,
src
.
backing_block_num
());
ASSERT_EQ
(
N
,
src
.
size
());
base
::
IOBufAsZeroCopyOutputStream
out_stream
(
&
src
);
out_stream
.
BackUp
(
1
);
// also succeed.
ASSERT_EQ
(
-
1
,
out_stream
.
ByteCount
());
ASSERT_EQ
(
base
::
IOBuf
::
DEFAULT_PAYLOAD
*
2
-
1
,
src
.
size
());
ASSERT_EQ
(
2u
,
src
.
backing_block_num
());
void
*
data0
=
NULL
;
int
size0
=
0
;
ASSERT_TRUE
(
out_stream
.
Next
(
&
data0
,
&
size0
));
ASSERT_EQ
(
1
,
size0
);
ASSERT_EQ
(
0
,
out_stream
.
ByteCount
());
ASSERT_EQ
(
2u
,
src
.
backing_block_num
());
void
*
data1
=
NULL
;
int
size1
=
0
;
ASSERT_TRUE
(
out_stream
.
Next
(
&
data1
,
&
size1
));
ASSERT_EQ
(
size1
,
out_stream
.
ByteCount
());
ASSERT_EQ
(
3u
,
src
.
backing_block_num
());
ASSERT_EQ
(
N
+
size1
,
src
.
size
());
void
*
data2
=
NULL
;
int
size2
=
0
;
ASSERT_TRUE
(
out_stream
.
Next
(
&
data2
,
&
size2
));
ASSERT_EQ
(
size1
+
size2
,
out_stream
.
ByteCount
());
ASSERT_EQ
(
4u
,
src
.
backing_block_num
());
ASSERT_EQ
(
N
+
size1
+
size2
,
src
.
size
());
LOG
(
INFO
)
<<
"Backup1"
;
out_stream
.
BackUp
(
size1
);
// intended size1, not size2 to make this BackUp
// to cross boundary of blocks.
ASSERT_EQ
(
size2
,
out_stream
.
ByteCount
());
ASSERT_EQ
(
N
+
size2
,
src
.
size
());
LOG
(
INFO
)
<<
"Backup2"
;
out_stream
.
BackUp
(
size2
);
ASSERT_EQ
(
0
,
out_stream
.
ByteCount
());
ASSERT_EQ
(
N
,
src
.
size
());
}
void
*
backup_thread
(
void
*
arg
)
{
base
::
IOBufAsZeroCopyOutputStream
*
wrapper
=
(
base
::
IOBufAsZeroCopyOutputStream
*
)
arg
;
wrapper
->
BackUp
(
1024
);
return
NULL
;
}
TEST_F
(
IOBufTest
,
backup_in_another_thread
)
{
base
::
IOBuf
buf
;
base
::
IOBufAsZeroCopyOutputStream
wrapper
(
&
buf
);
size_t
alloc_size
=
0
;
for
(
int
i
=
0
;
i
<
10
;
++
i
)
{
void
*
data
;
int
len
;
ASSERT_TRUE
(
wrapper
.
Next
(
&
data
,
&
len
));
alloc_size
+=
len
;
}
ASSERT_EQ
(
alloc_size
,
buf
.
length
());
for
(
int
i
=
0
;
i
<
10
;
++
i
)
{
void
*
data
;
int
len
;
ASSERT_TRUE
(
wrapper
.
Next
(
&
data
,
&
len
));
alloc_size
+=
len
;
pthread_t
tid
;
pthread_create
(
&
tid
,
NULL
,
backup_thread
,
&
wrapper
);
pthread_join
(
tid
,
NULL
);
}
ASSERT_EQ
(
alloc_size
-
1024
*
10
,
buf
.
length
());
}
TEST_F
(
IOBufTest
,
own_block
)
{
base
::
IOBuf
buf
;
const
ssize_t
BLOCK_SIZE
=
1024
;
base
::
IOBuf
::
Block
*
saved_tls_block
=
base
::
iobuf
::
get_tls_block_head
();
base
::
IOBufAsZeroCopyOutputStream
wrapper
(
&
buf
,
BLOCK_SIZE
);
int
alloc_size
=
0
;
for
(
int
i
=
0
;
i
<
100
;
++
i
)
{
void
*
data
;
int
size
;
ASSERT_TRUE
(
wrapper
.
Next
(
&
data
,
&
size
));
alloc_size
+=
size
;
if
(
size
>
1
)
{
wrapper
.
BackUp
(
1
);
alloc_size
-=
1
;
}
}
ASSERT_EQ
(
static_cast
<
size_t
>
(
alloc_size
),
buf
.
length
());
ASSERT_EQ
(
saved_tls_block
,
base
::
iobuf
::
get_tls_block_head
());
ASSERT_EQ
(
base
::
iobuf
::
block_cap
(
buf
.
_front_ref
().
block
),
BLOCK_SIZE
-
16
);
}
struct
Foo1
{
explicit
Foo1
(
int
x2
)
:
x
(
x2
)
{}
int
x
;
};
struct
Foo2
{
std
::
vector
<
Foo1
>
foo1
;
};
std
::
ostream
&
operator
<<
(
std
::
ostream
&
os
,
const
Foo1
&
foo1
)
{
return
os
<<
"foo1.x="
<<
foo1
.
x
;
}
std
::
ostream
&
operator
<<
(
std
::
ostream
&
os
,
const
Foo2
&
foo2
)
{
for
(
size_t
i
=
0
;
i
<
foo2
.
foo1
.
size
();
++
i
)
{
os
<<
"foo2["
<<
i
<<
"]="
<<
foo2
.
foo1
[
i
];
}
return
os
;
}
TEST_F
(
IOBufTest
,
as_ostream
)
{
base
::
iobuf
::
reset_blockmem_allocate_and_deallocate
();
base
::
IOBufBuilder
builder
;
LOG
(
INFO
)
<<
"sizeof(IOBufBuilder)="
<<
sizeof
(
builder
)
<<
std
::
endl
<<
"sizeof(IOBuf)="
<<
sizeof
(
base
::
IOBuf
)
<<
std
::
endl
<<
"sizeof(IOBufAsZeroCopyOutputStream)="
<<
sizeof
(
base
::
IOBufAsZeroCopyOutputStream
)
<<
std
::
endl
<<
"sizeof(ZeroCopyStreamAsStreamBuf)="
<<
sizeof
(
base
::
ZeroCopyStreamAsStreamBuf
)
<<
std
::
endl
<<
"sizeof(ostream)="
<<
sizeof
(
std
::
ostream
);
int
x
=
-
1
;
builder
<<
2
<<
" "
<<
x
<<
" "
<<
1.1
<<
" hello "
;
ASSERT_EQ
(
"2 -1 1.1 hello "
,
builder
.
buf
().
to_string
());
builder
<<
"world!"
;
ASSERT_EQ
(
"2 -1 1.1 hello world!"
,
builder
.
buf
().
to_string
());
builder
.
buf
().
clear
();
builder
<<
"world!"
;
ASSERT_EQ
(
"world!"
,
builder
.
buf
().
to_string
());
Foo2
foo2
;
for
(
int
i
=
0
;
i
<
10000
;
++
i
)
{
foo2
.
foo1
.
push_back
(
Foo1
(
i
));
}
builder
.
buf
().
clear
();
builder
<<
"<before>"
<<
foo2
<<
"<after>"
;
std
::
ostringstream
oss
;
oss
<<
"<before>"
<<
foo2
<<
"<after>"
;
ASSERT_EQ
(
oss
.
str
(),
builder
.
buf
().
to_string
());
base
::
IOBuf
target
;
builder
.
move_to
(
target
);
ASSERT_TRUE
(
builder
.
buf
().
empty
());
ASSERT_EQ
(
oss
.
str
(),
target
.
to_string
());
std
::
ostringstream
oss2
;
oss2
<<
target
;
ASSERT_EQ
(
oss
.
str
(),
oss2
.
str
());
}
TEST_F
(
IOBufTest
,
append_from_fd_with_offset
)
{
base
::
TempFile
file
;
file
.
save
(
"dummy"
);
base
::
fd_guard
fd
(
open
(
file
.
fname
(),
O_RDWR
|
O_TRUNC
));
ASSERT_TRUE
(
fd
>=
0
)
<<
file
.
fname
()
<<
' '
<<
berror
();
base
::
IOPortal
buf
;
char
dummy
[
10
*
1024
];
buf
.
append
(
dummy
,
sizeof
(
dummy
));
ASSERT_EQ
(
sizeof
(
dummy
),
buf
.
cut_into_file_descriptor
(
fd
));
for
(
size_t
i
=
0
;
i
<
sizeof
(
dummy
);
++
i
)
{
base
::
IOPortal
b0
;
ASSERT_EQ
(
sizeof
(
dummy
)
-
i
,
b0
.
pappend_from_file_descriptor
(
fd
,
i
,
sizeof
(
dummy
)))
<<
berror
();
char
tmp
[
sizeof
(
dummy
)];
ASSERT_EQ
(
0
,
memcmp
(
dummy
+
i
,
b0
.
fetch
(
tmp
,
b0
.
length
()),
b0
.
length
()));
}
}
static
base
::
atomic
<
int
>
s_nthread
(
0
);
static
long
number_per_thread
=
1024
;
void
*
cut_into_fd
(
void
*
arg
)
{
int
fd
=
(
int
)(
long
)
arg
;
const
long
start_num
=
number_per_thread
*
s_nthread
.
fetch_add
(
1
,
base
::
memory_order_relaxed
);
off_t
offset
=
start_num
*
sizeof
(
int
);
for
(
int
i
=
0
;
i
<
number_per_thread
;
++
i
)
{
int
to_write
=
start_num
+
i
;
base
::
IOBuf
out
;
out
.
append
(
&
to_write
,
sizeof
(
int
));
CHECK_EQ
(
out
.
pcut_into_file_descriptor
(
fd
,
offset
+
sizeof
(
int
)
*
i
),
sizeof
(
int
));
}
return
NULL
;
}
TEST_F
(
IOBufTest
,
cut_into_fd_with_offset_multithreaded
)
{
s_nthread
.
store
(
0
);
number_per_thread
=
10240
;
pthread_t
threads
[
8
];
long
fd
=
open
(
".out.txt"
,
O_RDWR
|
O_CREAT
|
O_TRUNC
,
0644
);
ASSERT_TRUE
(
fd
>=
0
)
<<
berror
();
for
(
size_t
i
=
0
;
i
<
ARRAY_SIZE
(
threads
);
++
i
)
{
ASSERT_EQ
(
0
,
pthread_create
(
&
threads
[
i
],
NULL
,
cut_into_fd
,
(
void
*
)
fd
));
}
for
(
size_t
i
=
0
;
i
<
ARRAY_SIZE
(
threads
);
++
i
)
{
pthread_join
(
threads
[
i
],
NULL
);
}
for
(
int
i
=
0
;
i
<
number_per_thread
*
(
int
)
ARRAY_SIZE
(
threads
);
++
i
)
{
off_t
offset
=
i
*
sizeof
(
int
);
base
::
IOPortal
in
;
ASSERT_EQ
(
sizeof
(
int
),
in
.
pappend_from_file_descriptor
(
fd
,
offset
,
sizeof
(
int
)));
int
j
;
ASSERT_EQ
(
sizeof
(
j
),
in
.
cutn
(
&
j
,
sizeof
(
j
)));
ASSERT_EQ
(
i
,
j
);
}
}
TEST_F
(
IOBufTest
,
slice
)
{
size_t
N
=
100000
;
std
::
string
expected
;
expected
.
reserve
(
N
);
base
::
IOBuf
buf
;
for
(
size_t
i
=
0
;
i
<
N
;
++
i
)
{
expected
.
push_back
(
i
%
26
+
'a'
);
buf
.
push_back
(
i
%
26
+
'a'
);
}
const
size_t
block_count
=
buf
.
backing_block_num
();
std
::
string
actual
;
actual
.
reserve
(
expected
.
size
());
for
(
size_t
i
=
0
;
i
<
block_count
;
++
i
)
{
base
::
StringPiece
p
=
buf
.
backing_block
(
i
);
ASSERT_FALSE
(
p
.
empty
());
actual
.
append
(
p
.
data
(),
p
.
size
());
}
ASSERT_TRUE
(
expected
==
actual
);
}
TEST_F
(
IOBufTest
,
swap
)
{
base
::
IOBuf
a
;
a
.
append
(
"I'am a"
);
base
::
IOBuf
b
;
b
.
append
(
"I'am b"
);
std
::
swap
(
a
,
b
);
ASSERT_TRUE
(
a
.
equals
(
"I'am b"
));
ASSERT_TRUE
(
b
.
equals
(
"I'am a"
));
}
TEST_F
(
IOBufTest
,
resize
)
{
base
::
IOBuf
a
;
a
.
resize
(
100
);
std
::
string
as
;
as
.
resize
(
100
);
base
::
IOBuf
b
;
b
.
resize
(
100
,
'b'
);
std
::
string
bs
;
bs
.
resize
(
100
,
'b'
);
ASSERT_EQ
(
100u
,
a
.
length
());
ASSERT_EQ
(
100u
,
b
.
length
());
ASSERT_TRUE
(
a
.
equals
(
as
));
ASSERT_TRUE
(
b
.
equals
(
bs
));
}
TEST_F
(
IOBufTest
,
iterate_bytes
)
{
base
::
IOBuf
a
;
a
.
append
(
"hello world"
);
std
::
string
saved_a
=
a
.
to_string
();
size_t
n
=
0
;
base
::
IOBufBytesIterator
it
(
a
);
for
(;
it
!=
NULL
;
++
it
,
++
n
)
{
ASSERT_EQ
(
saved_a
[
n
],
*
it
);
}
ASSERT_EQ
(
saved_a
.
size
(),
n
);
ASSERT_TRUE
(
saved_a
==
a
);
// append more to the iobuf, iterator should still be ended.
a
.
append
(
", this is iobuf"
);
ASSERT_TRUE
(
it
==
NULL
);
// append more-than-one-block data to the iobuf
for
(
int
i
=
0
;
i
<
1024
;
++
i
)
{
a
.
append
(
"ab33jm4hgaklkv;2[25lj4lkj312kl4j321kl4j3k"
);
}
saved_a
=
a
.
to_string
();
n
=
0
;
for
(
base
::
IOBufBytesIterator
it2
(
a
);
it2
!=
NULL
;
it2
++
/*intended post++*/
,
++
n
)
{
ASSERT_EQ
(
saved_a
[
n
],
*
it2
);
}
ASSERT_EQ
(
saved_a
.
size
(),
n
);
ASSERT_TRUE
(
saved_a
==
a
);
}
TEST_F
(
IOBufTest
,
appender
)
{
base
::
IOBufAppender
appender
;
ASSERT_EQ
(
0
,
appender
.
append
(
"hello"
,
5
));
ASSERT_EQ
(
"hello"
,
appender
.
buf
());
ASSERT_EQ
(
0
,
appender
.
push_back
(
' '
));
ASSERT_EQ
(
0
,
appender
.
append
(
"world"
,
5
));
ASSERT_EQ
(
"hello world"
,
appender
.
buf
());
base
::
IOBuf
buf2
;
appender
.
move_to
(
buf2
);
ASSERT_EQ
(
""
,
appender
.
buf
());
ASSERT_EQ
(
"hello world"
,
buf2
);
std
::
string
str
;
for
(
int
i
=
0
;
i
<
10000
;
++
i
)
{
char
buf
[
128
];
int
len
=
snprintf
(
buf
,
sizeof
(
buf
),
"1%d2%d3%d4%d5%d"
,
i
,
i
,
i
,
i
,
i
);
appender
.
append
(
buf
,
len
);
str
.
append
(
buf
,
len
);
}
ASSERT_EQ
(
str
,
appender
.
buf
());
base
::
IOBuf
buf3
;
appender
.
move_to
(
buf3
);
ASSERT_EQ
(
""
,
appender
.
buf
());
ASSERT_EQ
(
str
,
buf3
);
}
TEST_F
(
IOBufTest
,
appender_perf
)
{
const
size_t
N1
=
100000
;
base
::
Timer
tm1
;
tm1
.
start
();
base
::
IOBuf
buf1
;
for
(
size_t
i
=
0
;
i
<
N1
;
++
i
)
{
buf1
.
push_back
(
i
);
}
tm1
.
stop
();
base
::
Timer
tm2
;
tm2
.
start
();
base
::
IOBufAppender
appender1
;
for
(
size_t
i
=
0
;
i
<
N1
;
++
i
)
{
appender1
.
push_back
(
i
);
}
tm2
.
stop
();
LOG
(
INFO
)
<<
"IOBuf.push_back="
<<
tm1
.
n_elapsed
()
/
N1
<<
"ns IOBufAppender.push_back="
<<
tm2
.
n_elapsed
()
/
N1
<<
"ns"
;
const
size_t
N2
=
50000
;
const
std
::
string
s
=
"a repeatly appended string"
;
std
::
string
str2
;
base
::
IOBuf
buf2
;
tm1
.
start
();
for
(
size_t
i
=
0
;
i
<
N2
;
++
i
)
{
buf2
.
append
(
s
);
}
tm1
.
stop
();
tm2
.
start
();
base
::
IOBufAppender
appender2
;
for
(
size_t
i
=
0
;
i
<
N2
;
++
i
)
{
appender2
.
append
(
s
);
}
tm2
.
stop
();
base
::
Timer
tm3
;
tm3
.
start
();
for
(
size_t
i
=
0
;
i
<
N2
;
++
i
)
{
str2
.
append
(
s
);
}
tm3
.
stop
();
LOG
(
INFO
)
<<
"IOBuf.append="
<<
tm1
.
n_elapsed
()
/
N2
<<
"ns IOBufAppender.append="
<<
tm2
.
n_elapsed
()
/
N2
<<
"ns string.append="
<<
tm3
.
n_elapsed
()
/
N2
<<
"ns (string-length="
<<
s
.
size
()
<<
')'
;
}
TEST_F
(
IOBufTest
,
printed_as_binary
)
{
base
::
IOBuf
buf
;
std
::
string
str
;
for
(
int
i
=
0
;
i
<
256
;
++
i
)
{
buf
.
push_back
((
char
)
i
);
str
.
push_back
((
char
)
i
);
}
const
char
*
const
OUTPUT
=
"
\\
00
\\
01
\\
02
\\
03
\\
04
\\
05
\\
06
\\
07
\\
b
\\
t
\\
n
\\
0B
\\
0C
\\
r
\\
0E
\\
0F"
"
\\
10
\\
11
\\
12
\\
13
\\
14
\\
15
\\
16
\\
17
\\
18
\\
19
\\
1A
\\
1B
\\
1C
\\
1D
\\
1E"
"
\\
1F !
\"
#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUV"
"WXYZ[
\\\\
]^_`abcdefghijklmnopqrstuvwxyz{|}~
\\
7F
\\
80
\\
81
\\
82"
"
\\
83
\\
84
\\
85
\\
86
\\
87
\\
88
\\
89
\\
8A
\\
8B
\\
8C
\\
8D
\\
8E
\\
8F
\\
90
\\
91"
"
\\
92
\\
93
\\
94
\\
95
\\
96
\\
97
\\
98
\\
99
\\
9A
\\
9B
\\
9C
\\
9D
\\
9E
\\
9F
\\
A0"
"
\\
A1
\\
A2
\\
A3
\\
A4
\\
A5
\\
A6
\\
A7
\\
A8
\\
A9
\\
AA
\\
AB
\\
AC
\\
AD
\\
AE
\\
AF"
"
\\
B0
\\
B1
\\
B2
\\
B3
\\
B4
\\
B5
\\
B6
\\
B7
\\
B8
\\
B9
\\
BA
\\
BB
\\
BC
\\
BD
\\
BE"
"
\\
BF
\\
C0
\\
C1
\\
C2
\\
C3
\\
C4
\\
C5
\\
C6
\\
C7
\\
C8
\\
C9
\\
CA
\\
CB
\\
CC
\\
CD"
"
\\
CE
\\
CF
\\
D0
\\
D1
\\
D2
\\
D3
\\
D4
\\
D5
\\
D6
\\
D7
\\
D8
\\
D9
\\
DA
\\
DB
\\
DC"
"
\\
DD
\\
DE
\\
DF
\\
E0
\\
E1
\\
E2
\\
E3
\\
E4
\\
E5
\\
E6
\\
E7
\\
E8
\\
E9
\\
EA
\\
EB"
"
\\
EC
\\
ED
\\
EE
\\
EF
\\
F0
\\
F1
\\
F2
\\
F3
\\
F4
\\
F5
\\
F6
\\
F7
\\
F8
\\
F9
\\
FA"
"
\\
FB
\\
FC
\\
FD
\\
FE
\\
FF"
;
std
::
ostringstream
os
;
os
<<
base
::
PrintedAsBinary
(
buf
,
256
);
ASSERT_STREQ
(
OUTPUT
,
os
.
str
().
c_str
());
os
.
str
(
""
);
os
<<
base
::
PrintedAsBinary
(
str
,
256
);
ASSERT_STREQ
(
OUTPUT
,
os
.
str
().
c_str
());
}
TEST_F
(
IOBufTest
,
copy_to_string_from_iterator
)
{
base
::
IOBuf
b0
;
for
(
size_t
i
=
0
;
i
<
1
*
1024
*
1024lu
;
++
i
)
{
b0
.
push_back
(
base
::
fast_rand_in
(
'a'
,
'z'
));
}
base
::
IOBuf
b1
(
b0
);
base
::
IOBufBytesIterator
iter
(
b0
);
size_t
nc
=
0
;
while
(
nc
<
b0
.
length
())
{
size_t
to_copy
=
base
::
fast_rand_in
(
1024lu
,
64
*
1024lu
);
base
::
IOBuf
b
;
b1
.
cutn
(
&
b
,
to_copy
);
std
::
string
s
;
const
size_t
copied
=
iter
.
copy_and_forward
(
&
s
,
to_copy
);
ASSERT_GT
(
copied
,
0u
);
ASSERT_TRUE
(
b
.
equals
(
s
));
nc
+=
copied
;
}
ASSERT_EQ
(
nc
,
b0
.
length
());
}
}
// namespace
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment