Separate memory management and formatting

Array is split into an abstract Buffer class and a concrete MemoryBuffer class. BasicWriter now does all memory allocation through a Buffer object. Subclasses of BasicWriter may use different buffer types. The new BasicMemoryBuffer class uses the default MemoryBuffer.
This commit is contained in:
Victor Zverovich
2014-09-29 08:48:16 -07:00
parent 5ca3d00e26
commit d1ded569ff
11 changed files with 596 additions and 498 deletions
+10 -1
View File
@@ -27,12 +27,21 @@ foreach (target format-test printf-test)
set_target_properties(${target} PROPERTIES COMPILE_FLAGS ${CPP11_FLAG})
endif ()
endforeach ()
add_fmt_test(util-test)
add_fmt_test(util-test mock-allocator.h)
if (CPP11_FLAG)
set_target_properties(util-test PROPERTIES COMPILE_FLAGS ${CPP11_FLAG})
endif ()
foreach (src ${FMT_SOURCES})
set(FMT_TEST_SOURCES ${FMT_TEST_SOURCES} ../${src})
endforeach ()
include(CheckIncludeFileCXX)
check_include_file_cxx(type_traits HAVE_TYPE_TRAITS)
if (HAVE_TYPE_TRAITS)
add_definitions(-DFMT_USE_TYPE_TRAITS=1)
endif ()
add_executable(macro-test macro-test.cc ${FMT_TEST_SOURCES} ${TEST_MAIN_SRC})
set_target_properties(macro-test
PROPERTIES COMPILE_DEFINITIONS "FMT_USE_VARIADIC_TEMPLATES=0")
+3 -3
View File
@@ -88,20 +88,20 @@ TEST(FormatTest, StrError) {
TEST(FormatTest, FormatErrorCode) {
std::string msg = "error 42", sep = ": ";
{
fmt::Writer w;
fmt::MemoryWriter w;
w << "garbage";
format_error_code(w, 42, "test");
EXPECT_EQ("test: " + msg, w.str());
}
{
fmt::Writer w;
fmt::MemoryWriter w;
std::string prefix(
fmt::internal::INLINE_BUFFER_SIZE - msg.size() - sep.size() + 1, 'x');
format_error_code(w, 42, prefix);
EXPECT_EQ(msg, w.str());
}
{
fmt::Writer w;
fmt::MemoryWriter w;
std::string prefix(
fmt::internal::INLINE_BUFFER_SIZE - msg.size() - sep.size(), 'x');
format_error_code(w, 42, prefix);
+72 -346
View File
@@ -38,9 +38,9 @@
#include "gmock/gmock.h"
// Include format.cc instead of format.h to test implementation-specific stuff.
#include "format.h"
#include "util.h"
#include "mock-allocator.h"
#include "gtest-extra.h"
#if defined(_WIN32) && !defined(__MINGW32__)
@@ -58,17 +58,14 @@ FILE *safe_fopen(const char *filename, const char *mode) {
using std::size_t;
using fmt::internal::Array;
using fmt::BasicWriter;
using fmt::format;
using fmt::FormatError;
using fmt::StringRef;
using fmt::Writer;
using fmt::WWriter;
using fmt::MemoryWriter;
using fmt::WMemoryWriter;
using fmt::pad;
using testing::Return;
namespace {
// Checks if writing value to BasicWriter<Char> produces the same result
@@ -78,7 +75,8 @@ template <typename Char, typename T>
std::basic_ostringstream<Char> os;
os << value;
std::basic_string<Char> expected = os.str();
std::basic_string<Char> actual = (BasicWriter<Char>() << value).str();
std::basic_string<Char> actual =
(fmt::BasicMemoryWriter<Char>() << value).str();
if (expected == actual)
return ::testing::AssertionSuccess();
return ::testing::AssertionFailure()
@@ -126,280 +124,8 @@ class TestString {
}
};
template <typename T>
class MockAllocator {
public:
typedef T value_type;
MOCK_METHOD1_T(allocate, T* (std::size_t n));
MOCK_METHOD2_T(deallocate, void (T* p, std::size_t n));
};
template <typename Allocator>
class AllocatorRef {
private:
Allocator *alloc_;
public:
typedef typename Allocator::value_type value_type;
explicit AllocatorRef(Allocator *alloc = 0) : alloc_(alloc) {}
AllocatorRef(const AllocatorRef &other) : alloc_(other.alloc_) {}
AllocatorRef& operator=(const AllocatorRef &other) {
alloc_ = other.alloc_;
return *this;
}
#if FMT_USE_RVALUE_REFERENCES
private:
void move(AllocatorRef &other) {
alloc_ = other.alloc_;
other.alloc_ = 0;
}
public:
AllocatorRef(AllocatorRef &&other) {
move(other);
}
AllocatorRef& operator=(AllocatorRef &&other) {
assert(this != &other);
move(other);
return *this;
}
#endif
Allocator *get() const { return alloc_; }
value_type* allocate(std::size_t n) { return alloc_->allocate(n); }
void deallocate(value_type* p, std::size_t n) { alloc_->deallocate(p, n); }
};
void CheckForwarding(
MockAllocator<int> &alloc, AllocatorRef< MockAllocator<int> > &ref) {
int mem;
// Check if value_type is properly defined.
AllocatorRef< MockAllocator<int> >::value_type *ptr = &mem;
// Check forwarding.
EXPECT_CALL(alloc, allocate(42)).WillOnce(Return(ptr));
ref.allocate(42);
EXPECT_CALL(alloc, deallocate(ptr, 42));
ref.deallocate(ptr, 42);
}
TEST(AllocatorTest, AllocatorRef) {
testing::StrictMock< MockAllocator<int> > alloc;
typedef AllocatorRef< MockAllocator<int> > TestAllocatorRef;
TestAllocatorRef ref(&alloc);
// Check if AllocatorRef forwards to the underlying allocator.
CheckForwarding(alloc, ref);
TestAllocatorRef ref2(ref);
CheckForwarding(alloc, ref2);
TestAllocatorRef ref3;
EXPECT_EQ(0, ref3.get());
ref3 = ref;
CheckForwarding(alloc, ref3);
}
TEST(ArrayTest, Ctor) {
Array<char, 123> array;
EXPECT_EQ(0u, array.size());
EXPECT_EQ(123u, array.capacity());
}
#if FMT_USE_RVALUE_REFERENCES
typedef AllocatorRef< std::allocator<char> > TestAllocator;
void check_move_array(const char *str, Array<char, 5, TestAllocator> &array) {
std::allocator<char> *alloc = array.get_allocator().get();
Array<char, 5, TestAllocator> array2(std::move(array));
// Move shouldn't destroy the inline content of the first array.
EXPECT_EQ(str, std::string(&array[0], array.size()));
EXPECT_EQ(str, std::string(&array2[0], array2.size()));
EXPECT_EQ(5, array2.capacity());
// Move should transfer allocator.
EXPECT_EQ(0, array.get_allocator().get());
EXPECT_EQ(alloc, array2.get_allocator().get());
}
TEST(ArrayTest, MoveCtor) {
std::allocator<char> alloc;
Array<char, 5, TestAllocator> array((TestAllocator(&alloc)));
const char test[] = "test";
array.append(test, test + 4);
check_move_array("test", array);
// Adding one more character fills the inline buffer, but doesn't cause
// dynamic allocation.
array.push_back('a');
check_move_array("testa", array);
const char *inline_buffer_ptr = &array[0];
// Adding one more character causes the content to move from the inline to
// a dynamically allocated buffer.
array.push_back('b');
Array<char, 5, TestAllocator> array2(std::move(array));
// Move should rip the guts of the first array.
EXPECT_EQ(inline_buffer_ptr, &array[0]);
EXPECT_EQ("testab", std::string(&array2[0], array2.size()));
EXPECT_GT(array2.capacity(), 5u);
}
void check_move_assign_array(const char *str, Array<char, 5> &array) {
Array<char, 5> array2;
array2 = std::move(array);
// Move shouldn't destroy the inline content of the first array.
EXPECT_EQ(str, std::string(&array[0], array.size()));
EXPECT_EQ(str, std::string(&array2[0], array2.size()));
EXPECT_EQ(5, array2.capacity());
}
TEST(ArrayTest, MoveAssignment) {
Array<char, 5> array;
const char test[] = "test";
array.append(test, test + 4);
check_move_assign_array("test", array);
// Adding one more character fills the inline buffer, but doesn't cause
// dynamic allocation.
array.push_back('a');
check_move_assign_array("testa", array);
const char *inline_buffer_ptr = &array[0];
// Adding one more character causes the content to move from the inline to
// a dynamically allocated buffer.
array.push_back('b');
Array<char, 5> array2;
array2 = std::move(array);
// Move should rip the guts of the first array.
EXPECT_EQ(inline_buffer_ptr, &array[0]);
EXPECT_EQ("testab", std::string(&array2[0], array2.size()));
EXPECT_GT(array2.capacity(), 5u);
}
#endif // FMT_USE_RVALUE_REFERENCES
TEST(ArrayTest, Access) {
Array<char, 10> array;
array[0] = 11;
EXPECT_EQ(11, array[0]);
array[3] = 42;
EXPECT_EQ(42, *(&array[0] + 3));
const Array<char, 10> &carray = array;
EXPECT_EQ(42, carray[3]);
}
TEST(ArrayTest, Resize) {
Array<char, 123> array;
array[10] = 42;
EXPECT_EQ(42, array[10]);
array.resize(20);
EXPECT_EQ(20u, array.size());
EXPECT_EQ(123u, array.capacity());
EXPECT_EQ(42, array[10]);
array.resize(5);
EXPECT_EQ(5u, array.size());
EXPECT_EQ(123u, array.capacity());
EXPECT_EQ(42, array[10]);
}
TEST(ArrayTest, Grow) {
Array<int, 10> array;
array.resize(10);
for (int i = 0; i < 10; ++i)
array[i] = i * i;
array.resize(20);
EXPECT_EQ(20u, array.size());
EXPECT_EQ(20u, array.capacity());
for (int i = 0; i < 10; ++i)
EXPECT_EQ(i * i, array[i]);
}
TEST(ArrayTest, Clear) {
Array<char, 10> array;
array.resize(20);
array.clear();
EXPECT_EQ(0u, array.size());
EXPECT_EQ(20u, array.capacity());
}
TEST(ArrayTest, PushBack) {
Array<int, 10> array;
array.push_back(11);
EXPECT_EQ(11, array[0]);
EXPECT_EQ(1u, array.size());
array.resize(10);
array.push_back(22);
EXPECT_EQ(22, array[10]);
EXPECT_EQ(11u, array.size());
EXPECT_EQ(15u, array.capacity());
}
TEST(ArrayTest, Append) {
Array<char, 10> array;
const char *test = "test";
array.append(test, test + 5);
EXPECT_STREQ(test, &array[0]);
EXPECT_EQ(5u, array.size());
array.resize(10);
array.append(test, test + 2);
EXPECT_EQ('t', array[10]);
EXPECT_EQ('e', array[11]);
EXPECT_EQ(12u, array.size());
EXPECT_EQ(15u, array.capacity());
}
TEST(ArrayTest, AppendAllocatesEnoughStorage) {
Array<char, 10> array;
const char *test = "abcdefgh";
array.resize(10);
array.append(test, test + 9);
EXPECT_STREQ(test, &array[10]);
EXPECT_EQ(19u, array.size());
EXPECT_EQ(19u, array.capacity());
}
TEST(ArrayTest, Allocator) {
typedef AllocatorRef< MockAllocator<char> > TestAllocator;
Array<char, 10, TestAllocator> array;
EXPECT_EQ(0, array.get_allocator().get());
testing::StrictMock< MockAllocator<char> > alloc;
char mem;
{
Array<char, 10, TestAllocator> array2((TestAllocator(&alloc)));
EXPECT_EQ(&alloc, array2.get_allocator().get());
std::size_t size = 2 * fmt::internal::INLINE_BUFFER_SIZE;
EXPECT_CALL(alloc, allocate(size)).WillOnce(Return(&mem));
array2.reserve(size);
EXPECT_CALL(alloc, deallocate(&mem, size));
}
}
TEST(ArrayTest, ExceptionInDeallocate) {
typedef AllocatorRef< MockAllocator<char> > TestAllocator;
testing::StrictMock< MockAllocator<char> > alloc;
Array<char, 10, TestAllocator> array((TestAllocator(&alloc)));
std::size_t size = 2 * fmt::internal::INLINE_BUFFER_SIZE;
std::vector<char> mem(size);
{
EXPECT_CALL(alloc, allocate(size)).WillOnce(Return(&mem[0]));
array.resize(size);
std::fill(&array[0], &array[0] + size, 'x');
}
std::vector<char> mem2(2 * size);
{
EXPECT_CALL(alloc, allocate(2 * size)).WillOnce(Return(&mem2[0]));
std::exception e;
EXPECT_CALL(alloc, deallocate(&mem[0], size)).WillOnce(testing::Throw(e));
EXPECT_THROW(array.reserve(2 * size), std::exception);
EXPECT_EQ(&mem2[0], &array[0]);
// Check that the data has been copied.
for (std::size_t i = 0; i < size; ++i)
EXPECT_EQ('x', array[i]);
}
EXPECT_CALL(alloc, deallocate(&mem2[0], 2 * size));
}
TEST(WriterTest, Ctor) {
Writer w;
MemoryWriter w;
EXPECT_EQ(0u, w.size());
EXPECT_STREQ("", w.c_str());
EXPECT_EQ("", w.str());
@@ -407,15 +133,15 @@ TEST(WriterTest, Ctor) {
#if FMT_USE_RVALUE_REFERENCES
void check_move_writer(const std::string &str, Writer &w) {
Writer w2(std::move(w));
void check_move_writer(const std::string &str, MemoryWriter &w) {
MemoryWriter w2(std::move(w));
// Move shouldn't destroy the inline content of the first writer.
EXPECT_EQ(str, w.str());
EXPECT_EQ(str, w2.str());
}
TEST(WriterTest, MoveCtor) {
Writer w;
MemoryWriter w;
w << "test";
check_move_writer("test", w);
// This fills the inline buffer, but doesn't cause dynamic allocation.
@@ -429,14 +155,14 @@ TEST(WriterTest, MoveCtor) {
// Adding one more character causes the content to move from the inline to
// a dynamically allocated buffer.
w << '*';
Writer w2(std::move(w));
MemoryWriter w2(std::move(w));
// Move should rip the guts of the first writer.
EXPECT_EQ(inline_buffer_ptr, w.data());
EXPECT_EQ(s + '*', w2.str());
}
void CheckMoveAssignWriter(const std::string &str, Writer &w) {
Writer w2;
void CheckMoveAssignWriter(const std::string &str, MemoryWriter &w) {
MemoryWriter w2;
w2 = std::move(w);
// Move shouldn't destroy the inline content of the first writer.
EXPECT_EQ(str, w.str());
@@ -444,7 +170,7 @@ void CheckMoveAssignWriter(const std::string &str, Writer &w) {
}
TEST(WriterTest, MoveAssignment) {
Writer w;
MemoryWriter w;
w << "test";
CheckMoveAssignWriter("test", w);
// This fills the inline buffer, but doesn't cause dynamic allocation.
@@ -458,7 +184,7 @@ TEST(WriterTest, MoveAssignment) {
// Adding one more character causes the content to move from the inline to
// a dynamically allocated buffer.
w << '*';
Writer w2;
MemoryWriter w2;
w2 = std::move(w);
// Move should rip the guts of the first writer.
EXPECT_EQ(inline_buffer_ptr, w.data());
@@ -471,17 +197,17 @@ TEST(WriterTest, Allocator) {
typedef testing::StrictMock< MockAllocator<char> > MockAllocator;
typedef AllocatorRef<MockAllocator> TestAllocator;
MockAllocator alloc;
BasicWriter<char, TestAllocator> w((TestAllocator(&alloc)));
fmt::BasicMemoryWriter<char, TestAllocator> w((TestAllocator(&alloc)));
std::size_t size = 1.5 * fmt::internal::INLINE_BUFFER_SIZE;
std::vector<char> mem(size);
EXPECT_CALL(alloc, allocate(size)).WillOnce(Return(&mem[0]));
EXPECT_CALL(alloc, allocate(size)).WillOnce(testing::Return(&mem[0]));
for (int i = 0; i < fmt::internal::INLINE_BUFFER_SIZE + 1; ++i)
w << '*';
EXPECT_CALL(alloc, deallocate(&mem[0], size));
}
TEST(WriterTest, Data) {
Writer w;
MemoryWriter w;
w << 42;
EXPECT_EQ("42", std::string(w.data(), w.size()));
}
@@ -527,13 +253,13 @@ TEST(WriterTest, WriteLongDouble) {
}
TEST(WriterTest, WriteDoubleAtBufferBoundary) {
fmt::Writer writer;
MemoryWriter writer;
for (int i = 0; i < 100; ++i)
writer << 1.23456789;
}
TEST(WriterTest, WriteDoubleWithFilledBuffer) {
fmt::Writer writer;
MemoryWriter writer;
// Fill the buffer.
for (int i = 0; i < fmt::internal::INLINE_BUFFER_SIZE; ++i)
writer << ' ';
@@ -552,36 +278,36 @@ TEST(WriterTest, WriteWideChar) {
TEST(WriterTest, WriteString) {
CHECK_WRITE_CHAR("abc");
// The following line shouldn't compile:
//fmt::Writer() << L"abc";
//MemoryWriter() << L"abc";
}
TEST(WriterTest, WriteWideString) {
CHECK_WRITE_WCHAR(L"abc");
// The following line shouldn't compile:
//fmt::WWriter() << "abc";
//fmt::WMemoryWriter() << "abc";
}
TEST(WriterTest, bin) {
using fmt::bin;
EXPECT_EQ("1100101011111110", (Writer() << bin(0xcafe)).str());
EXPECT_EQ("1011101010111110", (Writer() << bin(0xbabeu)).str());
EXPECT_EQ("1101111010101101", (Writer() << bin(0xdeadl)).str());
EXPECT_EQ("1011111011101111", (Writer() << bin(0xbeeful)).str());
EXPECT_EQ("1100101011111110", (MemoryWriter() << bin(0xcafe)).str());
EXPECT_EQ("1011101010111110", (MemoryWriter() << bin(0xbabeu)).str());
EXPECT_EQ("1101111010101101", (MemoryWriter() << bin(0xdeadl)).str());
EXPECT_EQ("1011111011101111", (MemoryWriter() << bin(0xbeeful)).str());
EXPECT_EQ("11001010111111101011101010111110",
(Writer() << bin(0xcafebabell)).str());
(MemoryWriter() << bin(0xcafebabell)).str());
EXPECT_EQ("11011110101011011011111011101111",
(Writer() << bin(0xdeadbeefull)).str());
(MemoryWriter() << bin(0xdeadbeefull)).str());
}
TEST(WriterTest, oct) {
using fmt::oct;
EXPECT_EQ("12", (Writer() << oct(static_cast<short>(012))).str());
EXPECT_EQ("12", (Writer() << oct(012)).str());
EXPECT_EQ("34", (Writer() << oct(034u)).str());
EXPECT_EQ("56", (Writer() << oct(056l)).str());
EXPECT_EQ("70", (Writer() << oct(070ul)).str());
EXPECT_EQ("1234", (Writer() << oct(01234ll)).str());
EXPECT_EQ("5670", (Writer() << oct(05670ull)).str());
EXPECT_EQ("12", (MemoryWriter() << oct(static_cast<short>(012))).str());
EXPECT_EQ("12", (MemoryWriter() << oct(012)).str());
EXPECT_EQ("34", (MemoryWriter() << oct(034u)).str());
EXPECT_EQ("56", (MemoryWriter() << oct(056l)).str());
EXPECT_EQ("70", (MemoryWriter() << oct(070ul)).str());
EXPECT_EQ("1234", (MemoryWriter() << oct(01234ll)).str());
EXPECT_EQ("5670", (MemoryWriter() << oct(05670ull)).str());
}
TEST(WriterTest, hex) {
@@ -591,22 +317,22 @@ TEST(WriterTest, hex) {
// This shouldn't compile:
//fmt::IntFormatSpec<short, fmt::TypeSpec<'x'> > (*phex2)(short value) = hex;
EXPECT_EQ("cafe", (Writer() << hex(0xcafe)).str());
EXPECT_EQ("babe", (Writer() << hex(0xbabeu)).str());
EXPECT_EQ("dead", (Writer() << hex(0xdeadl)).str());
EXPECT_EQ("beef", (Writer() << hex(0xbeeful)).str());
EXPECT_EQ("cafebabe", (Writer() << hex(0xcafebabell)).str());
EXPECT_EQ("deadbeef", (Writer() << hex(0xdeadbeefull)).str());
EXPECT_EQ("cafe", (MemoryWriter() << hex(0xcafe)).str());
EXPECT_EQ("babe", (MemoryWriter() << hex(0xbabeu)).str());
EXPECT_EQ("dead", (MemoryWriter() << hex(0xdeadl)).str());
EXPECT_EQ("beef", (MemoryWriter() << hex(0xbeeful)).str());
EXPECT_EQ("cafebabe", (MemoryWriter() << hex(0xcafebabell)).str());
EXPECT_EQ("deadbeef", (MemoryWriter() << hex(0xdeadbeefull)).str());
}
TEST(WriterTest, hexu) {
using fmt::hexu;
EXPECT_EQ("CAFE", (Writer() << hexu(0xcafe)).str());
EXPECT_EQ("BABE", (Writer() << hexu(0xbabeu)).str());
EXPECT_EQ("DEAD", (Writer() << hexu(0xdeadl)).str());
EXPECT_EQ("BEEF", (Writer() << hexu(0xbeeful)).str());
EXPECT_EQ("CAFEBABE", (Writer() << hexu(0xcafebabell)).str());
EXPECT_EQ("DEADBEEF", (Writer() << hexu(0xdeadbeefull)).str());
EXPECT_EQ("CAFE", (MemoryWriter() << hexu(0xcafe)).str());
EXPECT_EQ("BABE", (MemoryWriter() << hexu(0xbabeu)).str());
EXPECT_EQ("DEAD", (MemoryWriter() << hexu(0xdeadl)).str());
EXPECT_EQ("BEEF", (MemoryWriter() << hexu(0xbeeful)).str());
EXPECT_EQ("CAFEBABE", (MemoryWriter() << hexu(0xcafebabell)).str());
EXPECT_EQ("DEADBEEF", (MemoryWriter() << hexu(0xdeadbeefull)).str());
}
class Date {
@@ -647,21 +373,21 @@ ISO8601DateFormatter iso8601(const Date &d) { return ISO8601DateFormatter(d); }
TEST(WriterTest, pad) {
using fmt::hex;
EXPECT_EQ(" cafe", (Writer() << pad(hex(0xcafe), 8)).str());
EXPECT_EQ(" babe", (Writer() << pad(hex(0xbabeu), 8)).str());
EXPECT_EQ(" dead", (Writer() << pad(hex(0xdeadl), 8)).str());
EXPECT_EQ(" beef", (Writer() << pad(hex(0xbeeful), 8)).str());
EXPECT_EQ(" dead", (Writer() << pad(hex(0xdeadll), 8)).str());
EXPECT_EQ(" beef", (Writer() << pad(hex(0xbeefull), 8)).str());
EXPECT_EQ(" cafe", (MemoryWriter() << pad(hex(0xcafe), 8)).str());
EXPECT_EQ(" babe", (MemoryWriter() << pad(hex(0xbabeu), 8)).str());
EXPECT_EQ(" dead", (MemoryWriter() << pad(hex(0xdeadl), 8)).str());
EXPECT_EQ(" beef", (MemoryWriter() << pad(hex(0xbeeful), 8)).str());
EXPECT_EQ(" dead", (MemoryWriter() << pad(hex(0xdeadll), 8)).str());
EXPECT_EQ(" beef", (MemoryWriter() << pad(hex(0xbeefull), 8)).str());
EXPECT_EQ(" 11", (Writer() << pad(11, 7)).str());
EXPECT_EQ(" 22", (Writer() << pad(22u, 7)).str());
EXPECT_EQ(" 33", (Writer() << pad(33l, 7)).str());
EXPECT_EQ(" 44", (Writer() << pad(44ul, 7)).str());
EXPECT_EQ(" 33", (Writer() << pad(33ll, 7)).str());
EXPECT_EQ(" 44", (Writer() << pad(44ull, 7)).str());
EXPECT_EQ(" 11", (MemoryWriter() << pad(11, 7)).str());
EXPECT_EQ(" 22", (MemoryWriter() << pad(22u, 7)).str());
EXPECT_EQ(" 33", (MemoryWriter() << pad(33l, 7)).str());
EXPECT_EQ(" 44", (MemoryWriter() << pad(44ul, 7)).str());
EXPECT_EQ(" 33", (MemoryWriter() << pad(33ll, 7)).str());
EXPECT_EQ(" 44", (MemoryWriter() << pad(44ull, 7)).str());
Writer w;
MemoryWriter w;
w.clear();
w << pad(42, 5, '0');
EXPECT_EQ("00042", w.str());
@@ -674,25 +400,25 @@ TEST(WriterTest, pad) {
}
TEST(WriterTest, PadString) {
EXPECT_EQ("test ", (Writer() << pad("test", 8)).str());
EXPECT_EQ("test******", (Writer() << pad("test", 10, '*')).str());
EXPECT_EQ("test ", (MemoryWriter() << pad("test", 8)).str());
EXPECT_EQ("test******", (MemoryWriter() << pad("test", 10, '*')).str());
}
TEST(WriterTest, PadWString) {
EXPECT_EQ(L"test ", (WWriter() << pad(L"test", 8)).str());
EXPECT_EQ(L"test******", (WWriter() << pad(L"test", 10, '*')).str());
EXPECT_EQ(L"test******", (WWriter() << pad(L"test", 10, L'*')).str());
EXPECT_EQ(L"test ", (WMemoryWriter() << pad(L"test", 8)).str());
EXPECT_EQ(L"test******", (WMemoryWriter() << pad(L"test", 10, '*')).str());
EXPECT_EQ(L"test******", (WMemoryWriter() << pad(L"test", 10, L'*')).str());
}
TEST(WriterTest, NoConflictWithIOManip) {
using namespace std;
using namespace fmt;
EXPECT_EQ("cafe", (Writer() << hex(0xcafe)).str());
EXPECT_EQ("12", (Writer() << oct(012)).str());
EXPECT_EQ("cafe", (MemoryWriter() << hex(0xcafe)).str());
EXPECT_EQ("12", (MemoryWriter() << oct(012)).str());
}
TEST(WriterTest, Format) {
Writer w;
MemoryWriter w;
w.write("part{0}", 1);
EXPECT_EQ(strlen("part1"), w.size());
EXPECT_STREQ("part1", w.c_str());
@@ -706,7 +432,7 @@ TEST(WriterTest, Format) {
}
TEST(WriterTest, WWriter) {
EXPECT_EQ(L"cafe", (fmt::WWriter() << fmt::hex(0xcafe)).str());
EXPECT_EQ(L"cafe", (fmt::WMemoryWriter() << fmt::hex(0xcafe)).str());
}
TEST(FormatterTest, Escape) {
@@ -1481,7 +1207,7 @@ TEST(FormatterTest, FormatStringFromSpeedTest) {
TEST(FormatterTest, FormatExamples) {
using fmt::hex;
EXPECT_EQ("0000cafe", (Writer() << pad(hex(0xcafe), 8, '0')).str());
EXPECT_EQ("0000cafe", (MemoryWriter() << pad(hex(0xcafe), 8, '0')).str());
std::string message = format("The answer is {}", 42);
EXPECT_EQ("The answer is 42", message);
@@ -1489,13 +1215,13 @@ TEST(FormatterTest, FormatExamples) {
EXPECT_EQ("42", format("{}", 42));
EXPECT_EQ("42", format(std::string("{}"), 42));
Writer out;
MemoryWriter out;
out << "The answer is " << 42 << "\n";
out.write("({:+f}, {:+f})", -3.14, 3.14);
EXPECT_EQ("The answer is 42\n(-3.140000, +3.140000)", out.str());
{
fmt::Writer writer;
MemoryWriter writer;
for (int i = 0; i < 10; i++)
writer.write("{}", i);
std::string s = writer.str(); // s == 0123456789
@@ -1655,7 +1381,7 @@ TEST(StrTest, Convert) {
std::string format_message(int id, const char *format,
const fmt::ArgList &args) {
fmt::Writer w;
MemoryWriter w;
w.write("[{}] ", id);
w.write(format, args);
return w.str();
+1 -1
View File
@@ -348,7 +348,7 @@ TEST(StreamingAssertionsTest, EXPECT_WRITE) {
}
TEST(UtilTest, FormatSystemError) {
fmt::Writer out;
fmt::MemoryWriter out;
fmt::internal::format_system_error(out, EDOM, "test message");
EXPECT_EQ(out.str(), format_system_error(EDOM, "test message"));
}
+1 -1
View File
@@ -92,7 +92,7 @@ std::string OutputRedirect::restore_and_read() {
#endif // FMT_USE_FILE_DESCRIPTORS
std::string format_system_error(int error_code, fmt::StringRef message) {
fmt::Writer out;
fmt::MemoryWriter out;
fmt::internal::format_system_error(out, error_code, message);
return out.str();
}
+3 -3
View File
@@ -25,8 +25,8 @@
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef FMT_GTEST_EXTRA_H
#define FMT_GTEST_EXTRA_H
#ifndef FMT_GTEST_EXTRA_H_
#define FMT_GTEST_EXTRA_H_
#include <string>
#include <gtest/gtest.h>
@@ -135,4 +135,4 @@ class OutputRedirect {
#endif // FMT_USE_FILE_DESCRIPTORS
#endif // FMT_GTEST_EXTRA_H
#endif // FMT_GTEST_EXTRA_H_
+83
View File
@@ -0,0 +1,83 @@
/*
Mock allocator.
Copyright (c) 2014, Victor Zverovich
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef FMT_MOCK_ALLOCATOR_H_
#define FMT_MOCK_ALLOCATOR_H_
#include "gmock/gmock.h"
template <typename T>
class MockAllocator {
public:
typedef T value_type;
MOCK_METHOD1_T(allocate, T* (std::size_t n));
MOCK_METHOD2_T(deallocate, void (T* p, std::size_t n));
};
template <typename Allocator>
class AllocatorRef {
private:
Allocator *alloc_;
public:
typedef typename Allocator::value_type value_type;
explicit AllocatorRef(Allocator *alloc = 0) : alloc_(alloc) {}
AllocatorRef(const AllocatorRef &other) : alloc_(other.alloc_) {}
AllocatorRef& operator=(const AllocatorRef &other) {
alloc_ = other.alloc_;
return *this;
}
#if FMT_USE_RVALUE_REFERENCES
private:
void move(AllocatorRef &other) {
alloc_ = other.alloc_;
other.alloc_ = 0;
}
public:
AllocatorRef(AllocatorRef &&other) {
move(other);
}
AllocatorRef& operator=(AllocatorRef &&other) {
assert(this != &other);
move(other);
return *this;
}
#endif
Allocator *get() const { return alloc_; }
value_type* allocate(std::size_t n) { return alloc_->allocate(n); }
void deallocate(value_type* p, std::size_t n) { alloc_->deallocate(p, n); }
};
#endif // FMT_MOCK_ALLOCATOR_H_
+250 -6
View File
@@ -29,7 +29,14 @@
#include <climits>
#include <cstring>
#include <limits>
#if FMT_USE_TYPE_TRAITS
# include <type_traits>
#endif
#include "gmock/gmock.h"
#include "gtest-extra.h"
#include "mock-allocator.h"
#include "util.h"
// Check if format.h compiles with windows.h included.
@@ -42,8 +49,11 @@
#undef max
using fmt::StringRef;
using fmt::internal::Value;
using fmt::internal::Arg;
using fmt::internal::Value;
using fmt::internal::MemoryBuffer;
using testing::Return;
namespace {
@@ -62,9 +72,241 @@ Arg make_arg(const T &value) {
fmt::internal::MakeValue<Char>::type(value));
return arg;
}
} // namespace
void CheckForwarding(
MockAllocator<int> &alloc, AllocatorRef< MockAllocator<int> > &ref) {
int mem;
// Check if value_type is properly defined.
AllocatorRef< MockAllocator<int> >::value_type *ptr = &mem;
// Check forwarding.
EXPECT_CALL(alloc, allocate(42)).WillOnce(Return(ptr));
ref.allocate(42);
EXPECT_CALL(alloc, deallocate(ptr, 42));
ref.deallocate(ptr, 42);
}
TEST(AllocatorTest, AllocatorRef) {
testing::StrictMock< MockAllocator<int> > alloc;
typedef AllocatorRef< MockAllocator<int> > TestAllocatorRef;
TestAllocatorRef ref(&alloc);
// Check if AllocatorRef forwards to the underlying allocator.
CheckForwarding(alloc, ref);
TestAllocatorRef ref2(ref);
CheckForwarding(alloc, ref2);
TestAllocatorRef ref3;
EXPECT_EQ(0, ref3.get());
ref3 = ref;
CheckForwarding(alloc, ref3);
}
#if FMT_USE_TYPE_TRAITS
TEST(BufferTest, NotCopyConstructible) {
EXPECT_FALSE(std::is_copy_constructible<fmt::internal::Buffer<char> >::value);
}
TEST(BufferTest, NotCopyAssignable) {
EXPECT_FALSE(std::is_copy_assignable<fmt::internal::Buffer<char> >::value);
}
#endif
TEST(MemoryBufferTest, Ctor) {
MemoryBuffer<char, 123> buffer;
EXPECT_EQ(0u, buffer.size());
EXPECT_EQ(123u, buffer.capacity());
}
#if FMT_USE_RVALUE_REFERENCES
typedef AllocatorRef< std::allocator<char> > TestAllocator;
void check_move_buffer(const char *str,
MemoryBuffer<char, 5, TestAllocator> &buffer) {
std::allocator<char> *alloc = buffer.get_allocator().get();
MemoryBuffer<char, 5, TestAllocator> buffer2(std::move(buffer));
// Move shouldn't destroy the inline content of the first buffer.
EXPECT_EQ(str, std::string(&buffer[0], buffer.size()));
EXPECT_EQ(str, std::string(&buffer2[0], buffer2.size()));
EXPECT_EQ(5, buffer2.capacity());
// Move should transfer allocator.
EXPECT_EQ(0, buffer.get_allocator().get());
EXPECT_EQ(alloc, buffer2.get_allocator().get());
}
TEST(MemoryBufferTest, MoveCtor) {
std::allocator<char> alloc;
MemoryBuffer<char, 5, TestAllocator> buffer((TestAllocator(&alloc)));
const char test[] = "test";
buffer.append(test, test + 4);
check_move_buffer("test", buffer);
// Adding one more character fills the inline buffer, but doesn't cause
// dynamic allocation.
buffer.push_back('a');
check_move_buffer("testa", buffer);
const char *inline_buffer_ptr = &buffer[0];
// Adding one more character causes the content to move from the inline to
// a dynamically allocated buffer.
buffer.push_back('b');
MemoryBuffer<char, 5, TestAllocator> buffer2(std::move(buffer));
// Move should rip the guts of the first buffer.
EXPECT_EQ(inline_buffer_ptr, &buffer[0]);
EXPECT_EQ("testab", std::string(&buffer2[0], buffer2.size()));
EXPECT_GT(buffer2.capacity(), 5u);
}
void check_move_assign_buffer(const char *str, MemoryBuffer<char, 5> &buffer) {
MemoryBuffer<char, 5> buffer2;
buffer2 = std::move(buffer);
// Move shouldn't destroy the inline content of the first buffer.
EXPECT_EQ(str, std::string(&buffer[0], buffer.size()));
EXPECT_EQ(str, std::string(&buffer2[0], buffer2.size()));
EXPECT_EQ(5, buffer2.capacity());
}
TEST(MemoryBufferTest, MoveAssignment) {
MemoryBuffer<char, 5> buffer;
const char test[] = "test";
buffer.append(test, test + 4);
check_move_assign_buffer("test", buffer);
// Adding one more character fills the inline buffer, but doesn't cause
// dynamic allocation.
buffer.push_back('a');
check_move_assign_buffer("testa", buffer);
const char *inline_buffer_ptr = &buffer[0];
// Adding one more character causes the content to move from the inline to
// a dynamically allocated buffer.
buffer.push_back('b');
MemoryBuffer<char, 5> buffer2;
buffer2 = std::move(buffer);
// Move should rip the guts of the first buffer.
EXPECT_EQ(inline_buffer_ptr, &buffer[0]);
EXPECT_EQ("testab", std::string(&buffer2[0], buffer2.size()));
EXPECT_GT(buffer2.capacity(), 5u);
}
#endif // FMT_USE_RVALUE_REFERENCES
TEST(MemoryBufferTest, Access) {
MemoryBuffer<char, 10> buffer;
buffer[0] = 11;
EXPECT_EQ(11, buffer[0]);
buffer[3] = 42;
EXPECT_EQ(42, *(&buffer[0] + 3));
const MemoryBuffer<char, 10> &const_buffer = buffer;
EXPECT_EQ(42, const_buffer[3]);
}
TEST(MemoryBufferTest, Resize) {
MemoryBuffer<char, 123> buffer;
buffer[10] = 42;
EXPECT_EQ(42, buffer[10]);
buffer.resize(20);
EXPECT_EQ(20u, buffer.size());
EXPECT_EQ(123u, buffer.capacity());
EXPECT_EQ(42, buffer[10]);
buffer.resize(5);
EXPECT_EQ(5u, buffer.size());
EXPECT_EQ(123u, buffer.capacity());
EXPECT_EQ(42, buffer[10]);
}
TEST(MemoryBufferTest, Grow) {
MemoryBuffer<int, 10> buffer;
buffer.resize(10);
for (int i = 0; i < 10; ++i)
buffer[i] = i * i;
buffer.resize(20);
EXPECT_EQ(20u, buffer.size());
EXPECT_EQ(20u, buffer.capacity());
for (int i = 0; i < 10; ++i)
EXPECT_EQ(i * i, buffer[i]);
}
TEST(MemoryBufferTest, Clear) {
MemoryBuffer<char, 10> buffer;
buffer.resize(20);
buffer.clear();
EXPECT_EQ(0u, buffer.size());
EXPECT_EQ(20u, buffer.capacity());
}
TEST(MemoryBufferTest, PushBack) {
MemoryBuffer<int, 10> buffer;
buffer.push_back(11);
EXPECT_EQ(11, buffer[0]);
EXPECT_EQ(1u, buffer.size());
buffer.resize(10);
buffer.push_back(22);
EXPECT_EQ(22, buffer[10]);
EXPECT_EQ(11u, buffer.size());
EXPECT_EQ(15u, buffer.capacity());
}
TEST(MemoryBufferTest, Append) {
MemoryBuffer<char, 10> buffer;
const char *test = "test";
buffer.append(test, test + 5);
EXPECT_STREQ(test, &buffer[0]);
EXPECT_EQ(5u, buffer.size());
buffer.resize(10);
buffer.append(test, test + 2);
EXPECT_EQ('t', buffer[10]);
EXPECT_EQ('e', buffer[11]);
EXPECT_EQ(12u, buffer.size());
EXPECT_EQ(15u, buffer.capacity());
}
TEST(MemoryBufferTest, AppendAllocatesEnoughStorage) {
MemoryBuffer<char, 10> buffer;
const char *test = "abcdefgh";
buffer.resize(10);
buffer.append(test, test + 9);
EXPECT_STREQ(test, &buffer[10]);
EXPECT_EQ(19u, buffer.size());
EXPECT_EQ(19u, buffer.capacity());
}
TEST(MemoryBufferTest, Allocator) {
typedef AllocatorRef< MockAllocator<char> > TestAllocator;
MemoryBuffer<char, 10, TestAllocator> buffer;
EXPECT_EQ(0, buffer.get_allocator().get());
testing::StrictMock< MockAllocator<char> > alloc;
char mem;
{
MemoryBuffer<char, 10, TestAllocator> buffer2((TestAllocator(&alloc)));
EXPECT_EQ(&alloc, buffer2.get_allocator().get());
std::size_t size = 2 * fmt::internal::INLINE_BUFFER_SIZE;
EXPECT_CALL(alloc, allocate(size)).WillOnce(Return(&mem));
buffer2.reserve(size);
EXPECT_CALL(alloc, deallocate(&mem, size));
}
}
TEST(MemoryBufferTest, ExceptionInDeallocate) {
typedef AllocatorRef< MockAllocator<char> > TestAllocator;
testing::StrictMock< MockAllocator<char> > alloc;
MemoryBuffer<char, 10, TestAllocator> buffer((TestAllocator(&alloc)));
std::size_t size = 2 * fmt::internal::INLINE_BUFFER_SIZE;
std::vector<char> mem(size);
{
EXPECT_CALL(alloc, allocate(size)).WillOnce(Return(&mem[0]));
buffer.resize(size);
std::fill(&buffer[0], &buffer[0] + size, 'x');
}
std::vector<char> mem2(2 * size);
{
EXPECT_CALL(alloc, allocate(2 * size)).WillOnce(Return(&mem2[0]));
std::exception e;
EXPECT_CALL(alloc, deallocate(&mem[0], size)).WillOnce(testing::Throw(e));
EXPECT_THROW(buffer.reserve(2 * size), std::exception);
EXPECT_EQ(&mem2[0], &buffer[0]);
// Check that the data has been copied.
for (std::size_t i = 0; i < size; ++i)
EXPECT_EQ('x', buffer[i]);
}
EXPECT_CALL(alloc, deallocate(&mem2[0], 2 * size));
}
TEST(UtilTest, Increment) {
char s[10] = "123";
increment(s);
@@ -236,7 +478,7 @@ TEST(ArgTest, MakeArg) {
Arg arg = make_arg<char>(t);
EXPECT_EQ(fmt::internal::Arg::CUSTOM, arg.type);
EXPECT_EQ(&t, arg.custom.value);
fmt::Writer w;
fmt::MemoryWriter w;
fmt::BasicFormatter<char> formatter(w);
const char *s = "}";
arg.custom.format(&formatter, &t, &s);
@@ -435,14 +677,14 @@ void check_throw_error(int error_code, FormatErrorMessage format) {
} catch (const fmt::SystemError &e) {
error = e;
}
fmt::Writer message;
fmt::MemoryWriter message;
format(message, error_code, "test error");
EXPECT_EQ(message.str(), error.what());
EXPECT_EQ(error_code, error.error_code());
}
TEST(UtilTest, FormatSystemError) {
fmt::Writer message;
fmt::MemoryWriter message;
fmt::internal::format_system_error(message, EDOM, "test");
EXPECT_EQ(fmt::format("test: {}", get_system_error(EDOM)), message.str());
message.clear();
@@ -459,7 +701,7 @@ TEST(UtilTest, SystemError) {
}
TEST(UtilTest, ReportSystemError) {
fmt::Writer out;
fmt::MemoryWriter out;
fmt::internal::format_system_error(out, EDOM, "test error");
out << '\n';
EXPECT_WRITE(stderr, fmt::report_system_error(EDOM, "test error"), out.str());
@@ -501,3 +743,5 @@ TEST(UtilTest, ReportWindowsError) {
}
#endif // _WIN32
// TODO: test Buffer