Add support for custom memory allocator to BasicWriter

This commit is contained in:
Victor Zverovich 2014-09-19 07:51:42 -07:00
parent 70205edd6e
commit b9a568b1dd
4 changed files with 345 additions and 302 deletions

223
format.cc
View File

@ -62,42 +62,8 @@ using fmt::internal::Arg;
namespace {
#ifndef _MSC_VER
// Portable version of signbit.
// When compiled in C++11 mode signbit is no longer a macro but a function
// defined in namespace std and the macro is undefined.
inline int getsign(double x) {
#ifdef signbit
return signbit(x);
#else
return std::signbit(x);
#endif
}
// Portable version of isinf.
#ifdef isinf
inline int isinfinity(double x) { return isinf(x); }
inline int isinfinity(long double x) { return isinf(x); }
#else
inline int isinfinity(double x) { return std::isinf(x); }
inline int isinfinity(long double x) { return std::isinf(x); }
#endif
#define FMT_SNPRINTF snprintf
# define FMT_SNPRINTF snprintf
#else // _MSC_VER
inline int getsign(double value) {
if (value < 0) return 1;
if (value == value) return 0;
int dec = 0, sign = 0;
char buffer[2]; // The buffer size must be >= 2 or _ecvt_s will fail.
_ecvt_s(buffer, sizeof(buffer), value, 0, &dec, &sign);
return sign;
}
inline int isinfinity(double x) { return !_finite(x); }
inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) {
va_list args;
va_start(args, format);
@ -105,16 +71,9 @@ inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) {
va_end(args);
return result;
}
#define FMT_SNPRINTF fmt_snprintf
# define FMT_SNPRINTF fmt_snprintf
#endif // _MSC_VER
template <typename T>
struct IsLongDouble { enum {VALUE = 0}; };
template <>
struct IsLongDouble<long double> { enum {VALUE = 1}; };
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template <bool IsSigned>
@ -600,175 +559,9 @@ class fmt::internal::ArgFormatter :
}
};
// Fills the padding around the content and returns the pointer to the
// content area.
template <typename Char>
typename fmt::BasicWriter<Char>::CharPtr
fmt::BasicWriter<Char>::fill_padding(CharPtr buffer,
unsigned total_size, std::size_t content_size, wchar_t fill) {
std::size_t padding = total_size - content_size;
std::size_t left_padding = padding / 2;
Char fill_char = static_cast<Char>(fill);
std::fill_n(buffer, left_padding, fill_char);
buffer += left_padding;
CharPtr content = buffer;
std::fill_n(buffer + content_size, padding - left_padding, fill_char);
return content;
}
template <typename Char>
template <typename T>
void fmt::BasicWriter<Char>::write_double(T value, const FormatSpec &spec) {
// Check type.
char type = spec.type();
bool upper = false;
switch (type) {
case 0:
type = 'g';
break;
case 'e': case 'f': case 'g': case 'a':
break;
case 'F':
#ifdef _MSC_VER
// MSVC's printf doesn't support 'F'.
type = 'f';
#endif
// Fall through.
case 'E': case 'G': case 'A':
upper = true;
break;
default:
internal::report_unknown_type(type, "double");
break;
}
char sign = 0;
// Use getsign instead of value < 0 because the latter is always
// false for NaN.
if (getsign(static_cast<double>(value))) {
sign = '-';
value = -value;
} else if (spec.flag(SIGN_FLAG)) {
sign = spec.flag(PLUS_FLAG) ? '+' : ' ';
}
if (value != value) {
// Format NaN ourselves because sprintf's output is not consistent
// across platforms.
std::size_t size = 4;
const char *nan = upper ? " NAN" : " nan";
if (!sign) {
--size;
++nan;
}
CharPtr out = write_str(nan, size, spec);
if (sign)
*out = sign;
return;
}
if (isinfinity(value)) {
// Format infinity ourselves because sprintf's output is not consistent
// across platforms.
std::size_t size = 4;
const char *inf = upper ? " INF" : " inf";
if (!sign) {
--size;
++inf;
}
CharPtr out = write_str(inf, size, spec);
if (sign)
*out = sign;
return;
}
std::size_t offset = buffer_.size();
unsigned width = spec.width();
if (sign) {
buffer_.reserve(buffer_.size() + (std::max)(width, 1u));
if (width > 0)
--width;
++offset;
}
// Build format string.
enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg
Char format[MAX_FORMAT_SIZE];
Char *format_ptr = format;
*format_ptr++ = '%';
unsigned width_for_sprintf = width;
if (spec.flag(HASH_FLAG))
*format_ptr++ = '#';
if (spec.align() == ALIGN_CENTER) {
width_for_sprintf = 0;
} else {
if (spec.align() == ALIGN_LEFT)
*format_ptr++ = '-';
if (width != 0)
*format_ptr++ = '*';
}
if (spec.precision() >= 0) {
*format_ptr++ = '.';
*format_ptr++ = '*';
}
if (IsLongDouble<T>::VALUE)
*format_ptr++ = 'L';
*format_ptr++ = type;
*format_ptr = '\0';
// Format using snprintf.
Char fill = static_cast<Char>(spec.fill());
for (;;) {
std::size_t size = buffer_.capacity() - offset;
#if _MSC_VER
// MSVC's vsnprintf_s doesn't work with zero size, so reserve
// space for at least one extra character to make the size non-zero.
// Note that the buffer's capacity will increase by more than 1.
if (size == 0) {
buffer_.reserve(offset + 1);
size = buffer_.capacity() - offset;
}
#endif
Char *start = &buffer_[offset];
int n = internal::CharTraits<Char>::format_float(
start, size, format, width_for_sprintf, spec.precision(), value);
if (n >= 0 && offset + n < buffer_.capacity()) {
if (sign) {
if ((spec.align() != ALIGN_RIGHT && spec.align() != ALIGN_DEFAULT) ||
*start != ' ') {
*(start - 1) = sign;
sign = 0;
} else {
*(start - 1) = fill;
}
++n;
}
if (spec.align() == ALIGN_CENTER &&
spec.width() > static_cast<unsigned>(n)) {
unsigned width = spec.width();
CharPtr p = grow_buffer(width);
std::copy(p, p + n, p + (width - n) / 2);
fill_padding(p, spec.width(), n, fill);
return;
}
if (spec.fill() != ' ' || sign) {
while (*start == ' ')
*start++ = fill;
if (sign)
*(start - 1) = sign;
}
grow_buffer(n);
return;
}
// If n is negative we ask to increase the capacity by at least 1,
// but as std::vector, the buffer grows exponentially.
buffer_.reserve(n >= 0 ? offset + n + 1 : buffer_.capacity() + 1);
}
}
template <typename Char>
template <typename Char, typename Allocator>
template <typename StrChar>
void fmt::BasicWriter<Char>::write_str(
void fmt::BasicWriter<Char, Allocator>::write_str(
const Arg::StringValue<StrChar> &str, const FormatSpec &spec) {
// Check if StrChar is convertible to Char.
internal::CharTraits<Char>::convert(StrChar());
@ -1259,10 +1052,6 @@ int fmt::fprintf(std::FILE *f, StringRef format, const ArgList &args) {
// Explicit instantiations for char.
template fmt::BasicWriter<char>::CharPtr
fmt::BasicWriter<char>::fill_padding(CharPtr buffer,
unsigned total_size, std::size_t content_size, wchar_t fill);
template void fmt::BasicFormatter<char>::format(
BasicStringRef<char> format, const ArgList &args);
@ -1271,10 +1060,6 @@ template void fmt::internal::PrintfFormatter<char>::format(
// Explicit instantiations for wchar_t.
template fmt::BasicWriter<wchar_t>::CharPtr
fmt::BasicWriter<wchar_t>::fill_padding(CharPtr buffer,
unsigned total_size, std::size_t content_size, wchar_t fill);
template void fmt::BasicFormatter<wchar_t>::format(
BasicStringRef<wchar_t> format, const ArgList &args);

268
format.h
View File

@ -31,6 +31,7 @@
#include <stdint.h>
#include <cassert>
#include <cmath>
#include <cstddef> // for std::ptrdiff_t
#include <cstdio>
#include <algorithm>
@ -122,7 +123,7 @@ FMT_GCC_EXTENSION typedef unsigned long long ULongLong;
using std::move;
#endif
template <typename Char>
template <typename Char, typename Allocator = std::allocator<Char> >
class BasicWriter;
typedef BasicWriter<char> Writer;
@ -253,8 +254,19 @@ class Array : private Allocator {
if (ptr_ != data_) this->deallocate(ptr_, capacity_);
}
FMT_DISALLOW_COPY_AND_ASSIGN(Array);
public:
explicit Array(const Allocator &alloc = Allocator())
: Allocator(alloc), size_(0), capacity_(SIZE), ptr_(data_) {}
~Array() { free(); }
#if FMT_USE_RVALUE_REFERENCES
private:
// Move data from other to this array.
void move(Array &other) {
Allocator &this_alloc = *this, &other_alloc = other;
this_alloc = std::move(other_alloc);
size_ = other.size_;
capacity_ = other.capacity_;
if (other.ptr_ == other.data_) {
@ -268,14 +280,7 @@ class Array : private Allocator {
}
}
FMT_DISALLOW_COPY_AND_ASSIGN(Array);
public:
explicit Array(const Allocator &alloc = Allocator())
: Allocator(alloc), size_(0), capacity_(SIZE), ptr_(data_) {}
~Array() { free(); }
#if FMT_USE_RVALUE_REFERENCES
Array(Array &&other) {
move(other);
}
@ -351,6 +356,44 @@ void Array<T, SIZE, Allocator>::append(const T *begin, const T *end) {
size_ += num_elements;
}
#ifndef _MSC_VER
// Portable version of signbit.
// When compiled in C++11 mode signbit is no longer a macro but a function
// defined in namespace std and the macro is undefined.
inline int getsign(double x) {
# ifdef signbit
return signbit(x);
# else
return std::signbit(x);
# endif
}
// Portable version of isinf.
# ifdef isinf
inline int isinfinity(double x) { return isinf(x); }
inline int isinfinity(long double x) { return isinf(x); }
# else
inline int isinfinity(double x) { return std::isinf(x); }
inline int isinfinity(long double x) { return std::isinf(x); }
# endif
#else
inline int getsign(double value) {
if (value < 0) return 1;
if (value == value) return 0;
int dec = 0, sign = 0;
char buffer[2]; // The buffer size must be >= 2 or _ecvt_s will fail.
_ecvt_s(buffer, sizeof(buffer), value, 0, &dec, &sign);
return sign;
}
inline int isinfinity(double x) { return !_finite(x); }
#endif
template <typename T>
struct IsLongDouble { enum {VALUE = 0}; };
template <>
struct IsLongDouble<long double> { enum {VALUE = 1}; };
template <typename Char>
class BasicCharTraits {
public:
@ -1274,15 +1317,16 @@ class SystemError : public internal::RuntimeError {
a character stream. The output is stored in a memory buffer that grows
dynamically.
You can use one of the following typedefs for common character types:
You can use one of the following typedefs for common character types
and the standard allocator:
+---------+----------------------+
| Type | Definition |
+=========+======================+
| Writer | BasicWriter<char> |
+---------+----------------------+
| WWriter | BasicWriter<wchar_t> |
+---------+----------------------+
+---------+-----------------------------------------------+
| Type | Definition |
+=========+===============================================+
| Writer | BasicWriter<char, std::allocator<char>> |
+---------+-----------------------------------------------+
| WWriter | BasicWriter<wchar_t, std::allocator<wchar_t>> |
+---------+-----------------------------------------------+
**Example**::
@ -1301,11 +1345,12 @@ class SystemError : public internal::RuntimeError {
accessed as a C string with ``out.c_str()``.
\endrst
*/
template <typename Char>
template <typename Char, typename Allocator>
class BasicWriter {
private:
// Output buffer.
mutable internal::Array<Char, internal::INLINE_BUFFER_SIZE> buffer_;
typedef internal::Array<Char, internal::INLINE_BUFFER_SIZE, Allocator> Array;
mutable Array buffer_;
typedef typename internal::CharTraits<Char>::CharPtr CharPtr;
@ -1316,6 +1361,8 @@ class BasicWriter {
static Char *get(Char *p) { return p; }
#endif
// Fills the padding around the content and returns the pointer to the
// content area.
static CharPtr fill_padding(CharPtr buffer,
unsigned total_size, std::size_t content_size, wchar_t fill);
@ -1370,7 +1417,7 @@ class BasicWriter {
/**
Constructs a ``BasicWriter`` object.
*/
BasicWriter() {}
BasicWriter(const Allocator &alloc = Allocator()) : buffer_(alloc) {}
#if FMT_USE_RVALUE_REFERENCES
/**
@ -1446,7 +1493,7 @@ class BasicWriter {
void write(BasicStringRef<Char> format, const ArgList &args) {
BasicFormatter<Char>(*this).format(format, args);
}
FMT_VARIADIC_VOID(write, fmt::BasicStringRef<Char>)
FMT_VARIADIC_VOID(write, BasicStringRef<Char>)
BasicWriter &operator<<(int value) {
return *this << IntFormatSpec<int>(value);
@ -1525,9 +1572,10 @@ class BasicWriter {
void clear() FMT_NOEXCEPT(true) { buffer_.clear(); }
};
template <typename Char>
template <typename Char, typename Allocator>
template <typename StrChar>
typename BasicWriter<Char>::CharPtr BasicWriter<Char>::write_str(
typename BasicWriter<Char, Allocator>::CharPtr
BasicWriter<Char, Allocator>::write_str(
const StrChar *s, std::size_t size, const AlignSpec &spec) {
CharPtr out = CharPtr();
if (spec.width() > size) {
@ -1548,10 +1596,25 @@ typename BasicWriter<Char>::CharPtr BasicWriter<Char>::write_str(
return out;
}
template <typename Char>
template <typename Char, typename Allocator>
typename BasicWriter<Char, Allocator>::CharPtr
BasicWriter<Char, Allocator>::fill_padding(
CharPtr buffer, unsigned total_size,
std::size_t content_size, wchar_t fill) {
std::size_t padding = total_size - content_size;
std::size_t left_padding = padding / 2;
Char fill_char = static_cast<Char>(fill);
std::fill_n(buffer, left_padding, fill_char);
buffer += left_padding;
CharPtr content = buffer;
std::fill_n(buffer + content_size, padding - left_padding, fill_char);
return content;
}
template <typename Char, typename Allocator>
template <typename Spec>
typename fmt::BasicWriter<Char>::CharPtr
fmt::BasicWriter<Char>::prepare_int_buffer(
typename BasicWriter<Char, Allocator>::CharPtr
BasicWriter<Char, Allocator>::prepare_int_buffer(
unsigned num_digits, const Spec &spec,
const char *prefix, unsigned prefix_size) {
unsigned width = spec.width();
@ -1611,9 +1674,9 @@ typename fmt::BasicWriter<Char>::CharPtr
return p - 1;
}
template <typename Char>
template <typename Char, typename Allocator>
template <typename T, typename Spec>
void BasicWriter<Char>::write_int(T value, const Spec &spec) {
void BasicWriter<Char, Allocator>::write_int(T value, const Spec &spec) {
unsigned prefix_size = 0;
typedef typename internal::IntTraits<T>::MainType UnsignedType;
UnsignedType abs_value = value;
@ -1693,6 +1756,157 @@ void BasicWriter<Char>::write_int(T value, const Spec &spec) {
}
}
template <typename Char, typename Allocator>
template <typename T>
void BasicWriter<Char, Allocator>::write_double(
T value, const FormatSpec &spec) {
// Check type.
char type = spec.type();
bool upper = false;
switch (type) {
case 0:
type = 'g';
break;
case 'e': case 'f': case 'g': case 'a':
break;
case 'F':
#ifdef _MSC_VER
// MSVC's printf doesn't support 'F'.
type = 'f';
#endif
// Fall through.
case 'E': case 'G': case 'A':
upper = true;
break;
default:
internal::report_unknown_type(type, "double");
break;
}
char sign = 0;
// Use getsign instead of value < 0 because the latter is always
// false for NaN.
if (internal::getsign(static_cast<double>(value))) {
sign = '-';
value = -value;
} else if (spec.flag(SIGN_FLAG)) {
sign = spec.flag(PLUS_FLAG) ? '+' : ' ';
}
if (value != value) {
// Format NaN ourselves because sprintf's output is not consistent
// across platforms.
std::size_t size = 4;
const char *nan = upper ? " NAN" : " nan";
if (!sign) {
--size;
++nan;
}
CharPtr out = write_str(nan, size, spec);
if (sign)
*out = sign;
return;
}
if (internal::isinfinity(value)) {
// Format infinity ourselves because sprintf's output is not consistent
// across platforms.
std::size_t size = 4;
const char *inf = upper ? " INF" : " inf";
if (!sign) {
--size;
++inf;
}
CharPtr out = write_str(inf, size, spec);
if (sign)
*out = sign;
return;
}
std::size_t offset = buffer_.size();
unsigned width = spec.width();
if (sign) {
buffer_.reserve(buffer_.size() + (std::max)(width, 1u));
if (width > 0)
--width;
++offset;
}
// Build format string.
enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg
Char format[MAX_FORMAT_SIZE];
Char *format_ptr = format;
*format_ptr++ = '%';
unsigned width_for_sprintf = width;
if (spec.flag(HASH_FLAG))
*format_ptr++ = '#';
if (spec.align() == ALIGN_CENTER) {
width_for_sprintf = 0;
} else {
if (spec.align() == ALIGN_LEFT)
*format_ptr++ = '-';
if (width != 0)
*format_ptr++ = '*';
}
if (spec.precision() >= 0) {
*format_ptr++ = '.';
*format_ptr++ = '*';
}
if (internal::IsLongDouble<T>::VALUE)
*format_ptr++ = 'L';
*format_ptr++ = type;
*format_ptr = '\0';
// Format using snprintf.
Char fill = static_cast<Char>(spec.fill());
for (;;) {
std::size_t size = buffer_.capacity() - offset;
#if _MSC_VER
// MSVC's vsnprintf_s doesn't work with zero size, so reserve
// space for at least one extra character to make the size non-zero.
// Note that the buffer's capacity will increase by more than 1.
if (size == 0) {
buffer_.reserve(offset + 1);
size = buffer_.capacity() - offset;
}
#endif
Char *start = &buffer_[offset];
int n = internal::CharTraits<Char>::format_float(
start, size, format, width_for_sprintf, spec.precision(), value);
if (n >= 0 && offset + n < buffer_.capacity()) {
if (sign) {
if ((spec.align() != ALIGN_RIGHT && spec.align() != ALIGN_DEFAULT) ||
*start != ' ') {
*(start - 1) = sign;
sign = 0;
} else {
*(start - 1) = fill;
}
++n;
}
if (spec.align() == ALIGN_CENTER &&
spec.width() > static_cast<unsigned>(n)) {
unsigned width = spec.width();
CharPtr p = grow_buffer(width);
std::copy(p, p + n, p + (width - n) / 2);
fill_padding(p, spec.width(), n, fill);
return;
}
if (spec.fill() != ' ' || sign) {
while (*start == ' ')
*start++ = fill;
if (sign)
*(start - 1) = sign;
}
grow_buffer(n);
return;
}
// If n is negative we ask to increase the capacity by at least 1,
// but as std::vector, the buffer grows exponentially.
buffer_.reserve(n >= 0 ? offset + n + 1 : buffer_.capacity() + 1);
}
}
// Formats a value.
template <typename Char, typename T>
void format(BasicFormatter<Char> &f, const Char *&format_str, const T &value) {

View File

@ -46,7 +46,7 @@ TEST(FormatTest, ArgConverter) {
TEST(FormatTest, FormatNegativeNaN) {
double nan = std::numeric_limits<double>::quiet_NaN();
if (getsign(-nan))
if (fmt::internal::getsign(-nan))
EXPECT_EQ("-nan", fmt::format("{}", -nan));
else
fmt::print("Warning: compiler doesn't handle negative NaN correctly");

View File

@ -126,6 +126,82 @@ 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());
@ -134,16 +210,23 @@ TEST(ArrayTest, Ctor) {
#if FMT_USE_RVALUE_REFERENCES
void check_move_array(const char *str, Array<char, 5> &array) {
Array<char, 5> array2(std::move(array));
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) {
Array<char, 5> array;
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);
@ -155,7 +238,7 @@ TEST(ArrayTest, MoveCtor) {
// 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(std::move(array));
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()));
@ -274,56 +357,6 @@ TEST(ArrayTest, AppendAllocatesEnoughStorage) {
EXPECT_EQ(19u, array.capacity());
}
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) {}
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, Allocator) {
typedef AllocatorRef< MockAllocator<char> > TestAllocator;
Array<char, 10, TestAllocator> array;
@ -340,7 +373,7 @@ TEST(ArrayTest, Allocator) {
}
}
TEST(ArrayTest, DeallocateException) {
TEST(ArrayTest, ExceptionInDeallocate) {
typedef AllocatorRef< MockAllocator<char> > TestAllocator;
testing::StrictMock< MockAllocator<char> > alloc;
Array<char, 10, TestAllocator> array((TestAllocator(&alloc)));
@ -434,6 +467,17 @@ TEST(WriterTest, MoveAssignment) {
#endif // FMT_USE_RVALUE_REFERENCES
TEST(WriterTest, Allocator) {
typedef AllocatorRef< MockAllocator<char> > TestAllocator;
MockAllocator<char> alloc;
BasicWriter<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]));
for (int i = 0; i < fmt::internal::INLINE_BUFFER_SIZE + 1; ++i)
w << '*';
}
TEST(WriterTest, Data) {
Writer w;
w << 42;