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:
parent
5ca3d00e26
commit
d1ded569ff
@ -24,6 +24,9 @@ else ()
|
|||||||
set(CPP11_FLAG -std=c++0x)
|
set(CPP11_FLAG -std=c++0x)
|
||||||
endif ()
|
endif ()
|
||||||
endif ()
|
endif ()
|
||||||
|
if (CPP11_FLAG)
|
||||||
|
set(CMAKE_REQUIRED_FLAGS ${CPP11_FLAG})
|
||||||
|
endif ()
|
||||||
|
|
||||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
|
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
"${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||||
|
16
format.cc
16
format.cc
@ -154,7 +154,7 @@ void format_error_code(fmt::Writer &out, int error_code,
|
|||||||
|
|
||||||
void report_error(FormatFunc func,
|
void report_error(FormatFunc func,
|
||||||
int error_code, fmt::StringRef message) FMT_NOEXCEPT(true) {
|
int error_code, fmt::StringRef message) FMT_NOEXCEPT(true) {
|
||||||
fmt::Writer full_message;
|
fmt::MemoryWriter full_message;
|
||||||
func(full_message, error_code, message);
|
func(full_message, error_code, message);
|
||||||
// Use Writer::data instead of Writer::c_str to avoid potential memory
|
// Use Writer::data instead of Writer::c_str to avoid potential memory
|
||||||
// allocation.
|
// allocation.
|
||||||
@ -321,7 +321,7 @@ inline Arg::StringValue<wchar_t> ignore_incompatible_str(
|
|||||||
void fmt::SystemError::init(
|
void fmt::SystemError::init(
|
||||||
int error_code, StringRef format_str, ArgList args) {
|
int error_code, StringRef format_str, ArgList args) {
|
||||||
error_code_ = error_code;
|
error_code_ = error_code;
|
||||||
Writer w;
|
MemoryWriter w;
|
||||||
internal::format_system_error(w, error_code, format(format_str, args));
|
internal::format_system_error(w, error_code, format(format_str, args));
|
||||||
std::runtime_error &base = *this;
|
std::runtime_error &base = *this;
|
||||||
base = std::runtime_error(w.str());
|
base = std::runtime_error(w.str());
|
||||||
@ -442,7 +442,7 @@ void fmt::internal::format_system_error(
|
|||||||
fmt::Writer &out, int error_code,
|
fmt::Writer &out, int error_code,
|
||||||
fmt::StringRef message) FMT_NOEXCEPT(true) {
|
fmt::StringRef message) FMT_NOEXCEPT(true) {
|
||||||
try {
|
try {
|
||||||
Array<char, INLINE_BUFFER_SIZE> buffer;
|
MemoryBuffer<char, INLINE_BUFFER_SIZE> buffer;
|
||||||
buffer.resize(INLINE_BUFFER_SIZE);
|
buffer.resize(INLINE_BUFFER_SIZE);
|
||||||
char *system_message = 0;
|
char *system_message = 0;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
@ -559,9 +559,9 @@ class fmt::internal::ArgFormatter :
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char, typename Allocator>
|
template <typename Char>
|
||||||
template <typename StrChar>
|
template <typename StrChar>
|
||||||
void fmt::BasicWriter<Char, Allocator>::write_str(
|
void fmt::BasicWriter<Char>::write_str(
|
||||||
const Arg::StringValue<StrChar> &str, const FormatSpec &spec) {
|
const Arg::StringValue<StrChar> &str, const FormatSpec &spec) {
|
||||||
// Check if StrChar is convertible to Char.
|
// Check if StrChar is convertible to Char.
|
||||||
internal::CharTraits<Char>::convert(StrChar());
|
internal::CharTraits<Char>::convert(StrChar());
|
||||||
@ -1029,7 +1029,7 @@ void fmt::report_windows_error(
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
void fmt::print(std::FILE *f, StringRef format_str, ArgList args) {
|
void fmt::print(std::FILE *f, StringRef format_str, ArgList args) {
|
||||||
Writer w;
|
MemoryWriter w;
|
||||||
w.write(format_str, args);
|
w.write(format_str, args);
|
||||||
std::fwrite(w.data(), 1, w.size(), f);
|
std::fwrite(w.data(), 1, w.size(), f);
|
||||||
}
|
}
|
||||||
@ -1039,7 +1039,7 @@ void fmt::print(StringRef format_str, ArgList args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void fmt::print(std::ostream &os, StringRef format_str, ArgList args) {
|
void fmt::print(std::ostream &os, StringRef format_str, ArgList args) {
|
||||||
Writer w;
|
MemoryWriter w;
|
||||||
w.write(format_str, args);
|
w.write(format_str, args);
|
||||||
os.write(w.data(), w.size());
|
os.write(w.data(), w.size());
|
||||||
}
|
}
|
||||||
@ -1053,7 +1053,7 @@ void fmt::print_colored(Color c, StringRef format, ArgList args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int fmt::fprintf(std::FILE *f, StringRef format, ArgList args) {
|
int fmt::fprintf(std::FILE *f, StringRef format, ArgList args) {
|
||||||
Writer w;
|
MemoryWriter w;
|
||||||
printf(w, format, args);
|
printf(w, format, args);
|
||||||
return std::fwrite(w.data(), 1, w.size(), f);
|
return std::fwrite(w.data(), 1, w.size(), f);
|
||||||
}
|
}
|
||||||
|
291
format.h
291
format.h
@ -123,7 +123,7 @@ FMT_GCC_EXTENSION typedef unsigned long long ULongLong;
|
|||||||
using std::move;
|
using std::move;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
template <typename Char, typename Allocator = std::allocator<Char> >
|
template <typename Char>
|
||||||
class BasicWriter;
|
class BasicWriter;
|
||||||
|
|
||||||
typedef BasicWriter<char> Writer;
|
typedef BasicWriter<char> Writer;
|
||||||
@ -222,8 +222,8 @@ public:
|
|||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
// The number of characters to store in the Array object, representing the
|
// The number of characters to store in the MemoryBuffer object itself
|
||||||
// output buffer, itself to avoid dynamic memory allocation.
|
// to avoid dynamic memory allocation.
|
||||||
enum { INLINE_BUFFER_SIZE = 500 };
|
enum { INLINE_BUFFER_SIZE = 500 };
|
||||||
|
|
||||||
#if _SECURE_SCL
|
#if _SECURE_SCL
|
||||||
@ -237,72 +237,32 @@ template <typename T>
|
|||||||
inline T *make_ptr(T *ptr, std::size_t) { return ptr; }
|
inline T *make_ptr(T *ptr, std::size_t) { return ptr; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// A simple array for POD types with the first SIZE elements stored in
|
// A buffer for POD types. It supports a subset of std::vector's operations.
|
||||||
// the object itself. It supports a subset of std::vector's operations.
|
template <typename T>
|
||||||
template <typename T, std::size_t SIZE, typename Allocator = std::allocator<T> >
|
class Buffer {
|
||||||
class Array : private Allocator {
|
|
||||||
private:
|
private:
|
||||||
|
FMT_DISALLOW_COPY_AND_ASSIGN(Buffer);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
T *ptr_;
|
||||||
std::size_t size_;
|
std::size_t size_;
|
||||||
std::size_t capacity_;
|
std::size_t capacity_;
|
||||||
T *ptr_;
|
|
||||||
T data_[SIZE];
|
|
||||||
|
|
||||||
void grow(std::size_t size);
|
Buffer(T *ptr = 0, std::size_t capacity = 0)
|
||||||
|
: ptr_(ptr), size_(0), capacity_(capacity) {}
|
||||||
|
|
||||||
// Free memory allocated by the array.
|
virtual void grow(std::size_t size) = 0;
|
||||||
void free() {
|
|
||||||
if (ptr_ != data_) this->deallocate(ptr_, capacity_);
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_DISALLOW_COPY_AND_ASSIGN(Array);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit Array(const Allocator &alloc = Allocator())
|
virtual ~Buffer() {}
|
||||||
: Allocator(alloc), size_(0), capacity_(SIZE), ptr_(data_) {}
|
|
||||||
~Array() { free(); }
|
|
||||||
|
|
||||||
#if FMT_USE_RVALUE_REFERENCES
|
// Returns the size of this buffer.
|
||||||
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_) {
|
|
||||||
ptr_ = data_;
|
|
||||||
std::copy(other.data_, other.data_ + size_, make_ptr(data_, capacity_));
|
|
||||||
} else {
|
|
||||||
ptr_ = other.ptr_;
|
|
||||||
// Set pointer to the inline array so that delete is not called
|
|
||||||
// when freeing.
|
|
||||||
other.ptr_ = other.data_;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
Array(Array &&other) {
|
|
||||||
move(other);
|
|
||||||
}
|
|
||||||
|
|
||||||
Array& operator=(Array &&other) {
|
|
||||||
assert(this != &other);
|
|
||||||
free();
|
|
||||||
move(other);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Returns the size of this array.
|
|
||||||
std::size_t size() const { return size_; }
|
std::size_t size() const { return size_; }
|
||||||
|
|
||||||
// Returns the capacity of this array.
|
// Returns the capacity of this buffer.
|
||||||
std::size_t capacity() const { return capacity_; }
|
std::size_t capacity() const { return capacity_; }
|
||||||
|
|
||||||
// Returns a copy of the allocator associated with this array.
|
// Resizes the buffer. If T is a POD type new elements are not initialized.
|
||||||
Allocator get_allocator() const { return *this; }
|
|
||||||
|
|
||||||
// Resizes the array. If T is a POD type new elements are not initialized.
|
|
||||||
void resize(std::size_t new_size) {
|
void resize(std::size_t new_size) {
|
||||||
if (new_size > capacity_)
|
if (new_size > capacity_)
|
||||||
grow(new_size);
|
grow(new_size);
|
||||||
@ -323,32 +283,15 @@ class Array : private Allocator {
|
|||||||
ptr_[size_++] = value;
|
ptr_[size_++] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Appends data to the end of the array.
|
// Appends data to the end of the buffer.
|
||||||
void append(const T *begin, const T *end);
|
void append(const T *begin, const T *end);
|
||||||
|
|
||||||
T &operator[](std::size_t index) { return ptr_[index]; }
|
T &operator[](std::size_t index) { return ptr_[index]; }
|
||||||
const T &operator[](std::size_t index) const { return ptr_[index]; }
|
const T &operator[](std::size_t index) const { return ptr_[index]; }
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, std::size_t SIZE, typename Allocator>
|
template <typename T>
|
||||||
void Array<T, SIZE, Allocator>::grow(std::size_t size) {
|
void Buffer<T>::append(const T *begin, const T *end) {
|
||||||
std::size_t new_capacity = (std::max)(size, capacity_ + capacity_ / 2);
|
|
||||||
T *new_ptr = this->allocate(new_capacity);
|
|
||||||
// The following code doesn't throw, so the raw pointer above doesn't leak.
|
|
||||||
std::copy(ptr_, ptr_ + size_, make_ptr(new_ptr, new_capacity));
|
|
||||||
std::size_t old_capacity = capacity_;
|
|
||||||
T *old_ptr = ptr_;
|
|
||||||
capacity_ = new_capacity;
|
|
||||||
ptr_ = new_ptr;
|
|
||||||
// deallocate may throw (at least in principle), but it doesn't matter since
|
|
||||||
// the array already uses the new storage and will deallocate it in case
|
|
||||||
// of exception.
|
|
||||||
if (old_ptr != data_)
|
|
||||||
this->deallocate(old_ptr, old_capacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, std::size_t SIZE, typename Allocator>
|
|
||||||
void Array<T, SIZE, Allocator>::append(const T *begin, const T *end) {
|
|
||||||
std::ptrdiff_t num_elements = end - begin;
|
std::ptrdiff_t num_elements = end - begin;
|
||||||
if (size_ + num_elements > capacity_)
|
if (size_ + num_elements > capacity_)
|
||||||
grow(size_ + num_elements);
|
grow(size_ + num_elements);
|
||||||
@ -356,6 +299,81 @@ void Array<T, SIZE, Allocator>::append(const T *begin, const T *end) {
|
|||||||
size_ += num_elements;
|
size_ += num_elements;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A memory buffer for POD types with the first SIZE elements stored in
|
||||||
|
// the object itself.
|
||||||
|
template <typename T, std::size_t SIZE, typename Allocator = std::allocator<T> >
|
||||||
|
class MemoryBuffer : private Allocator, public Buffer<T> {
|
||||||
|
private:
|
||||||
|
T data_[SIZE];
|
||||||
|
|
||||||
|
void grow(std::size_t size);
|
||||||
|
|
||||||
|
// Free memory allocated by the buffer.
|
||||||
|
void free() {
|
||||||
|
if (this->ptr_ != data_) this->deallocate(this->ptr_, this->capacity_);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit MemoryBuffer(const Allocator &alloc = Allocator())
|
||||||
|
: Allocator(alloc), Buffer<T>(data_, SIZE) {}
|
||||||
|
~MemoryBuffer() { free(); }
|
||||||
|
|
||||||
|
#if FMT_USE_RVALUE_REFERENCES
|
||||||
|
private:
|
||||||
|
// Move data from other to this buffer.
|
||||||
|
void move(MemoryBuffer &other) {
|
||||||
|
Allocator &this_alloc = *this, &other_alloc = other;
|
||||||
|
this_alloc = std::move(other_alloc);
|
||||||
|
this->size_ = other.size_;
|
||||||
|
this->capacity_ = other.capacity_;
|
||||||
|
if (other.ptr_ == other.data_) {
|
||||||
|
this->ptr_ = data_;
|
||||||
|
std::copy(other.data_,
|
||||||
|
other.data_ + this->size_, make_ptr(data_, this->capacity_));
|
||||||
|
} else {
|
||||||
|
this->ptr_ = other.ptr_;
|
||||||
|
// Set pointer to the inline array so that delete is not called
|
||||||
|
// when freeing.
|
||||||
|
other.ptr_ = other.data_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
MemoryBuffer(MemoryBuffer &&other) {
|
||||||
|
move(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryBuffer &operator=(MemoryBuffer &&other) {
|
||||||
|
assert(this != &other);
|
||||||
|
free();
|
||||||
|
move(other);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Returns a copy of the allocator associated with this buffer.
|
||||||
|
Allocator get_allocator() const { return *this; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, std::size_t SIZE, typename Allocator>
|
||||||
|
void MemoryBuffer<T, SIZE, Allocator>::grow(std::size_t size) {
|
||||||
|
std::size_t new_capacity =
|
||||||
|
(std::max)(size, this->capacity_ + this->capacity_ / 2);
|
||||||
|
T *new_ptr = this->allocate(new_capacity);
|
||||||
|
// The following code doesn't throw, so the raw pointer above doesn't leak.
|
||||||
|
std::copy(this->ptr_,
|
||||||
|
this->ptr_ + this->size_, make_ptr(new_ptr, new_capacity));
|
||||||
|
std::size_t old_capacity = this->capacity_;
|
||||||
|
T *old_ptr = this->ptr_;
|
||||||
|
this->capacity_ = new_capacity;
|
||||||
|
this->ptr_ = new_ptr;
|
||||||
|
// deallocate may throw (at least in principle), but it doesn't matter since
|
||||||
|
// the buffer already uses the new storage and will deallocate it in case
|
||||||
|
// of exception.
|
||||||
|
if (old_ptr != data_)
|
||||||
|
this->deallocate(old_ptr, old_capacity);
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
// Portable version of signbit.
|
// Portable version of signbit.
|
||||||
// When compiled in C++11 mode signbit is no longer a macro but a function
|
// When compiled in C++11 mode signbit is no longer a macro but a function
|
||||||
@ -437,24 +455,17 @@ class CharTraits<wchar_t> : public BasicCharTraits<wchar_t> {
|
|||||||
const wchar_t *format, unsigned width, int precision, T value);
|
const wchar_t *format, unsigned width, int precision, T value);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Selects uint32_t if FitsIn32Bits is true, uint64_t otherwise.
|
|
||||||
template <bool FitsIn32Bits>
|
|
||||||
struct TypeSelector { typedef uint32_t Type; };
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct TypeSelector<false> { typedef uint64_t Type; };
|
|
||||||
|
|
||||||
// Checks if a number is negative - used to avoid warnings.
|
// Checks if a number is negative - used to avoid warnings.
|
||||||
template <bool IsSigned>
|
template <bool IsSigned>
|
||||||
struct SignChecker {
|
struct SignChecker {
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static bool is_negative(T) { return false; }
|
static bool is_negative(T value) { return value < 0; }
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct SignChecker<true> {
|
struct SignChecker<false> {
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static bool is_negative(T value) { return value < 0; }
|
static bool is_negative(T) { return false; }
|
||||||
};
|
};
|
||||||
|
|
||||||
// Returns true if value is negative, false otherwise.
|
// Returns true if value is negative, false otherwise.
|
||||||
@ -464,6 +475,13 @@ inline bool is_negative(T value) {
|
|||||||
return SignChecker<std::numeric_limits<T>::is_signed>::is_negative(value);
|
return SignChecker<std::numeric_limits<T>::is_signed>::is_negative(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Selects uint32_t if FitsIn32Bits is true, uint64_t otherwise.
|
||||||
|
template <bool FitsIn32Bits>
|
||||||
|
struct TypeSelector { typedef uint32_t Type; };
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct TypeSelector<false> { typedef uint64_t Type; };
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct IntTraits {
|
struct IntTraits {
|
||||||
// Smallest of uint32_t and uint64_t that is large enough to represent
|
// Smallest of uint32_t and uint64_t that is large enough to represent
|
||||||
@ -556,7 +574,7 @@ inline void format_decimal(Char *buffer, UInt value, unsigned num_digits) {
|
|||||||
// It is only provided for Windows since other systems support UTF-8 natively.
|
// It is only provided for Windows since other systems support UTF-8 natively.
|
||||||
class UTF8ToUTF16 {
|
class UTF8ToUTF16 {
|
||||||
private:
|
private:
|
||||||
Array<wchar_t, INLINE_BUFFER_SIZE> buffer_;
|
MemoryBuffer<wchar_t, INLINE_BUFFER_SIZE> buffer_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit UTF8ToUTF16(StringRef s);
|
explicit UTF8ToUTF16(StringRef s);
|
||||||
@ -570,7 +588,7 @@ class UTF8ToUTF16 {
|
|||||||
// It is only provided for Windows since other systems support UTF-8 natively.
|
// It is only provided for Windows since other systems support UTF-8 natively.
|
||||||
class UTF16ToUTF8 {
|
class UTF16ToUTF8 {
|
||||||
private:
|
private:
|
||||||
Array<char, INLINE_BUFFER_SIZE> buffer_;
|
MemoryBuffer<char, INLINE_BUFFER_SIZE> buffer_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
UTF16ToUTF8() {}
|
UTF16ToUTF8() {}
|
||||||
@ -608,7 +626,7 @@ struct NonZero<0> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// The value of a formatting argument. It is a POD type to allow storage in
|
// The value of a formatting argument. It is a POD type to allow storage in
|
||||||
// internal::Array.
|
// internal::MemoryBuffer.
|
||||||
struct Value {
|
struct Value {
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
struct StringValue {
|
struct StringValue {
|
||||||
@ -1421,12 +1439,13 @@ class SystemError : public internal::RuntimeError {
|
|||||||
accessed as a C string with ``out.c_str()``.
|
accessed as a C string with ``out.c_str()``.
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
template <typename Char, typename Allocator>
|
template <typename Char>
|
||||||
class BasicWriter {
|
class BasicWriter {
|
||||||
private:
|
private:
|
||||||
// Output buffer.
|
// Output buffer.
|
||||||
typedef internal::Array<Char, internal::INLINE_BUFFER_SIZE, Allocator> Array;
|
internal::Buffer<Char> &buffer_;
|
||||||
mutable Array buffer_;
|
|
||||||
|
FMT_DISALLOW_COPY_AND_ASSIGN(BasicWriter);
|
||||||
|
|
||||||
typedef typename internal::CharTraits<Char>::CharPtr CharPtr;
|
typedef typename internal::CharTraits<Char>::CharPtr CharPtr;
|
||||||
|
|
||||||
@ -1465,7 +1484,7 @@ class BasicWriter {
|
|||||||
|
|
||||||
// Formats an integer.
|
// Formats an integer.
|
||||||
template <typename T, typename Spec>
|
template <typename T, typename Spec>
|
||||||
void write_int(T value, const Spec &spec);
|
void write_int(T value, Spec spec);
|
||||||
|
|
||||||
// Formats a floating-point number (double or long double).
|
// Formats a floating-point number (double or long double).
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@ -1493,24 +1512,7 @@ class BasicWriter {
|
|||||||
/**
|
/**
|
||||||
Constructs a ``BasicWriter`` object.
|
Constructs a ``BasicWriter`` object.
|
||||||
*/
|
*/
|
||||||
BasicWriter(const Allocator &alloc = Allocator()) : buffer_(alloc) {}
|
explicit BasicWriter(internal::Buffer<Char> &b) : buffer_(b) {}
|
||||||
|
|
||||||
#if FMT_USE_RVALUE_REFERENCES
|
|
||||||
/**
|
|
||||||
Constructs a ``BasicWriter`` object moving the content of the other
|
|
||||||
object to it.
|
|
||||||
*/
|
|
||||||
BasicWriter(BasicWriter &&other) : buffer_(std::move(other.buffer_)) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Moves the content of the other ``BasicWriter`` object to this one.
|
|
||||||
*/
|
|
||||||
BasicWriter& operator=(BasicWriter &&other) {
|
|
||||||
assert(this != &other);
|
|
||||||
buffer_ = std::move(other.buffer_);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Returns the total number of characters written.
|
Returns the total number of characters written.
|
||||||
@ -1631,7 +1633,7 @@ class BasicWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename Spec, typename FillChar>
|
template <typename T, typename Spec, typename FillChar>
|
||||||
BasicWriter &operator<<(const IntFormatSpec<T, Spec, FillChar> &spec) {
|
BasicWriter &operator<<(IntFormatSpec<T, Spec, FillChar> spec) {
|
||||||
internal::CharTraits<Char>::convert(FillChar());
|
internal::CharTraits<Char>::convert(FillChar());
|
||||||
write_int(spec.value(), spec);
|
write_int(spec.value(), spec);
|
||||||
return *this;
|
return *this;
|
||||||
@ -1648,10 +1650,10 @@ class BasicWriter {
|
|||||||
void clear() FMT_NOEXCEPT(true) { buffer_.clear(); }
|
void clear() FMT_NOEXCEPT(true) { buffer_.clear(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char, typename Allocator>
|
template <typename Char>
|
||||||
template <typename StrChar>
|
template <typename StrChar>
|
||||||
typename BasicWriter<Char, Allocator>::CharPtr
|
typename BasicWriter<Char>::CharPtr
|
||||||
BasicWriter<Char, Allocator>::write_str(
|
BasicWriter<Char>::write_str(
|
||||||
const StrChar *s, std::size_t size, const AlignSpec &spec) {
|
const StrChar *s, std::size_t size, const AlignSpec &spec) {
|
||||||
CharPtr out = CharPtr();
|
CharPtr out = CharPtr();
|
||||||
if (spec.width() > size) {
|
if (spec.width() > size) {
|
||||||
@ -1672,9 +1674,9 @@ typename BasicWriter<Char, Allocator>::CharPtr
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char, typename Allocator>
|
template <typename Char>
|
||||||
typename BasicWriter<Char, Allocator>::CharPtr
|
typename BasicWriter<Char>::CharPtr
|
||||||
BasicWriter<Char, Allocator>::fill_padding(
|
BasicWriter<Char>::fill_padding(
|
||||||
CharPtr buffer, unsigned total_size,
|
CharPtr buffer, unsigned total_size,
|
||||||
std::size_t content_size, wchar_t fill) {
|
std::size_t content_size, wchar_t fill) {
|
||||||
std::size_t padding = total_size - content_size;
|
std::size_t padding = total_size - content_size;
|
||||||
@ -1687,10 +1689,10 @@ typename BasicWriter<Char, Allocator>::CharPtr
|
|||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char, typename Allocator>
|
template <typename Char>
|
||||||
template <typename Spec>
|
template <typename Spec>
|
||||||
typename BasicWriter<Char, Allocator>::CharPtr
|
typename BasicWriter<Char>::CharPtr
|
||||||
BasicWriter<Char, Allocator>::prepare_int_buffer(
|
BasicWriter<Char>::prepare_int_buffer(
|
||||||
unsigned num_digits, const Spec &spec,
|
unsigned num_digits, const Spec &spec,
|
||||||
const char *prefix, unsigned prefix_size) {
|
const char *prefix, unsigned prefix_size) {
|
||||||
unsigned width = spec.width();
|
unsigned width = spec.width();
|
||||||
@ -1750,9 +1752,9 @@ typename BasicWriter<Char, Allocator>::CharPtr
|
|||||||
return p - 1;
|
return p - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char, typename Allocator>
|
template <typename Char>
|
||||||
template <typename T, typename Spec>
|
template <typename T, typename Spec>
|
||||||
void BasicWriter<Char, Allocator>::write_int(T value, const Spec &spec) {
|
void BasicWriter<Char>::write_int(T value, Spec spec) {
|
||||||
unsigned prefix_size = 0;
|
unsigned prefix_size = 0;
|
||||||
typedef typename internal::IntTraits<T>::MainType UnsignedType;
|
typedef typename internal::IntTraits<T>::MainType UnsignedType;
|
||||||
UnsignedType abs_value = value;
|
UnsignedType abs_value = value;
|
||||||
@ -1832,9 +1834,9 @@ void BasicWriter<Char, Allocator>::write_int(T value, const Spec &spec) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char, typename Allocator>
|
template <typename Char>
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void BasicWriter<Char, Allocator>::write_double(
|
void BasicWriter<Char>::write_double(
|
||||||
T value, const FormatSpec &spec) {
|
T value, const FormatSpec &spec) {
|
||||||
// Check type.
|
// Check type.
|
||||||
char type = spec.type();
|
char type = spec.type();
|
||||||
@ -1983,6 +1985,37 @@ void BasicWriter<Char, Allocator>::write_double(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Char, typename Allocator = std::allocator<Char> >
|
||||||
|
class BasicMemoryWriter : public BasicWriter<Char> {
|
||||||
|
private:
|
||||||
|
internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE, Allocator> buffer_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit BasicMemoryWriter(const Allocator& alloc = Allocator())
|
||||||
|
: BasicWriter<Char>(buffer_), buffer_(alloc) {}
|
||||||
|
|
||||||
|
#if FMT_USE_RVALUE_REFERENCES
|
||||||
|
/**
|
||||||
|
Constructs a ``BasicMemoryWriter`` object moving the content of the other
|
||||||
|
object to it.
|
||||||
|
*/
|
||||||
|
BasicMemoryWriter(BasicMemoryWriter &&other)
|
||||||
|
: BasicWriter<Char>(buffer_), buffer_(std::move(other.buffer_)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Moves the content of the other ``BasicMemoryWriter`` object to this one.
|
||||||
|
*/
|
||||||
|
BasicMemoryWriter &operator=(BasicMemoryWriter &&other) {
|
||||||
|
buffer_ = std::move(other.buffer_);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef BasicMemoryWriter<char> MemoryWriter;
|
||||||
|
typedef BasicMemoryWriter<wchar_t> WMemoryWriter;
|
||||||
|
|
||||||
// Formats a value.
|
// Formats a value.
|
||||||
template <typename Char, typename T>
|
template <typename Char, typename T>
|
||||||
void format(BasicFormatter<Char> &f, const Char *&format_str, const T &value) {
|
void format(BasicFormatter<Char> &f, const Char *&format_str, const T &value) {
|
||||||
@ -2051,13 +2084,13 @@ void print_colored(Color c, StringRef format, ArgList args);
|
|||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
inline std::string format(StringRef format_str, ArgList args) {
|
inline std::string format(StringRef format_str, ArgList args) {
|
||||||
Writer w;
|
MemoryWriter w;
|
||||||
w.write(format_str, args);
|
w.write(format_str, args);
|
||||||
return w.str();
|
return w.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::wstring format(WStringRef format_str, ArgList args) {
|
inline std::wstring format(WStringRef format_str, ArgList args) {
|
||||||
WWriter w;
|
WMemoryWriter w;
|
||||||
w.write(format_str, args);
|
w.write(format_str, args);
|
||||||
return w.str();
|
return w.str();
|
||||||
}
|
}
|
||||||
@ -2110,7 +2143,7 @@ void printf(BasicWriter<Char> &w, BasicStringRef<Char> format, ArgList args) {
|
|||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
inline std::string sprintf(StringRef format, ArgList args) {
|
inline std::string sprintf(StringRef format, ArgList args) {
|
||||||
Writer w;
|
MemoryWriter w;
|
||||||
printf(w, format, args);
|
printf(w, format, args);
|
||||||
return w.str();
|
return w.str();
|
||||||
}
|
}
|
||||||
|
@ -27,12 +27,21 @@ foreach (target format-test printf-test)
|
|||||||
set_target_properties(${target} PROPERTIES COMPILE_FLAGS ${CPP11_FLAG})
|
set_target_properties(${target} PROPERTIES COMPILE_FLAGS ${CPP11_FLAG})
|
||||||
endif ()
|
endif ()
|
||||||
endforeach ()
|
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})
|
foreach (src ${FMT_SOURCES})
|
||||||
set(FMT_TEST_SOURCES ${FMT_TEST_SOURCES} ../${src})
|
set(FMT_TEST_SOURCES ${FMT_TEST_SOURCES} ../${src})
|
||||||
endforeach ()
|
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})
|
add_executable(macro-test macro-test.cc ${FMT_TEST_SOURCES} ${TEST_MAIN_SRC})
|
||||||
set_target_properties(macro-test
|
set_target_properties(macro-test
|
||||||
PROPERTIES COMPILE_DEFINITIONS "FMT_USE_VARIADIC_TEMPLATES=0")
|
PROPERTIES COMPILE_DEFINITIONS "FMT_USE_VARIADIC_TEMPLATES=0")
|
||||||
|
@ -88,20 +88,20 @@ TEST(FormatTest, StrError) {
|
|||||||
TEST(FormatTest, FormatErrorCode) {
|
TEST(FormatTest, FormatErrorCode) {
|
||||||
std::string msg = "error 42", sep = ": ";
|
std::string msg = "error 42", sep = ": ";
|
||||||
{
|
{
|
||||||
fmt::Writer w;
|
fmt::MemoryWriter w;
|
||||||
w << "garbage";
|
w << "garbage";
|
||||||
format_error_code(w, 42, "test");
|
format_error_code(w, 42, "test");
|
||||||
EXPECT_EQ("test: " + msg, w.str());
|
EXPECT_EQ("test: " + msg, w.str());
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
fmt::Writer w;
|
fmt::MemoryWriter w;
|
||||||
std::string prefix(
|
std::string prefix(
|
||||||
fmt::internal::INLINE_BUFFER_SIZE - msg.size() - sep.size() + 1, 'x');
|
fmt::internal::INLINE_BUFFER_SIZE - msg.size() - sep.size() + 1, 'x');
|
||||||
format_error_code(w, 42, prefix);
|
format_error_code(w, 42, prefix);
|
||||||
EXPECT_EQ(msg, w.str());
|
EXPECT_EQ(msg, w.str());
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
fmt::Writer w;
|
fmt::MemoryWriter w;
|
||||||
std::string prefix(
|
std::string prefix(
|
||||||
fmt::internal::INLINE_BUFFER_SIZE - msg.size() - sep.size(), 'x');
|
fmt::internal::INLINE_BUFFER_SIZE - msg.size() - sep.size(), 'x');
|
||||||
format_error_code(w, 42, prefix);
|
format_error_code(w, 42, prefix);
|
||||||
|
@ -38,9 +38,9 @@
|
|||||||
|
|
||||||
#include "gmock/gmock.h"
|
#include "gmock/gmock.h"
|
||||||
|
|
||||||
// Include format.cc instead of format.h to test implementation-specific stuff.
|
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include "mock-allocator.h"
|
||||||
#include "gtest-extra.h"
|
#include "gtest-extra.h"
|
||||||
|
|
||||||
#if defined(_WIN32) && !defined(__MINGW32__)
|
#if defined(_WIN32) && !defined(__MINGW32__)
|
||||||
@ -58,17 +58,14 @@ FILE *safe_fopen(const char *filename, const char *mode) {
|
|||||||
|
|
||||||
using std::size_t;
|
using std::size_t;
|
||||||
|
|
||||||
using fmt::internal::Array;
|
|
||||||
using fmt::BasicWriter;
|
using fmt::BasicWriter;
|
||||||
using fmt::format;
|
using fmt::format;
|
||||||
using fmt::FormatError;
|
using fmt::FormatError;
|
||||||
using fmt::StringRef;
|
using fmt::StringRef;
|
||||||
using fmt::Writer;
|
using fmt::MemoryWriter;
|
||||||
using fmt::WWriter;
|
using fmt::WMemoryWriter;
|
||||||
using fmt::pad;
|
using fmt::pad;
|
||||||
|
|
||||||
using testing::Return;
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// Checks if writing value to BasicWriter<Char> produces the same result
|
// 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;
|
std::basic_ostringstream<Char> os;
|
||||||
os << value;
|
os << value;
|
||||||
std::basic_string<Char> expected = os.str();
|
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)
|
if (expected == actual)
|
||||||
return ::testing::AssertionSuccess();
|
return ::testing::AssertionSuccess();
|
||||||
return ::testing::AssertionFailure()
|
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) {
|
TEST(WriterTest, Ctor) {
|
||||||
Writer w;
|
MemoryWriter w;
|
||||||
EXPECT_EQ(0u, w.size());
|
EXPECT_EQ(0u, w.size());
|
||||||
EXPECT_STREQ("", w.c_str());
|
EXPECT_STREQ("", w.c_str());
|
||||||
EXPECT_EQ("", w.str());
|
EXPECT_EQ("", w.str());
|
||||||
@ -407,15 +133,15 @@ TEST(WriterTest, Ctor) {
|
|||||||
|
|
||||||
#if FMT_USE_RVALUE_REFERENCES
|
#if FMT_USE_RVALUE_REFERENCES
|
||||||
|
|
||||||
void check_move_writer(const std::string &str, Writer &w) {
|
void check_move_writer(const std::string &str, MemoryWriter &w) {
|
||||||
Writer w2(std::move(w));
|
MemoryWriter w2(std::move(w));
|
||||||
// Move shouldn't destroy the inline content of the first writer.
|
// Move shouldn't destroy the inline content of the first writer.
|
||||||
EXPECT_EQ(str, w.str());
|
EXPECT_EQ(str, w.str());
|
||||||
EXPECT_EQ(str, w2.str());
|
EXPECT_EQ(str, w2.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(WriterTest, MoveCtor) {
|
TEST(WriterTest, MoveCtor) {
|
||||||
Writer w;
|
MemoryWriter w;
|
||||||
w << "test";
|
w << "test";
|
||||||
check_move_writer("test", w);
|
check_move_writer("test", w);
|
||||||
// This fills the inline buffer, but doesn't cause dynamic allocation.
|
// 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
|
// Adding one more character causes the content to move from the inline to
|
||||||
// a dynamically allocated buffer.
|
// a dynamically allocated buffer.
|
||||||
w << '*';
|
w << '*';
|
||||||
Writer w2(std::move(w));
|
MemoryWriter w2(std::move(w));
|
||||||
// Move should rip the guts of the first writer.
|
// Move should rip the guts of the first writer.
|
||||||
EXPECT_EQ(inline_buffer_ptr, w.data());
|
EXPECT_EQ(inline_buffer_ptr, w.data());
|
||||||
EXPECT_EQ(s + '*', w2.str());
|
EXPECT_EQ(s + '*', w2.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckMoveAssignWriter(const std::string &str, Writer &w) {
|
void CheckMoveAssignWriter(const std::string &str, MemoryWriter &w) {
|
||||||
Writer w2;
|
MemoryWriter w2;
|
||||||
w2 = std::move(w);
|
w2 = std::move(w);
|
||||||
// Move shouldn't destroy the inline content of the first writer.
|
// Move shouldn't destroy the inline content of the first writer.
|
||||||
EXPECT_EQ(str, w.str());
|
EXPECT_EQ(str, w.str());
|
||||||
@ -444,7 +170,7 @@ void CheckMoveAssignWriter(const std::string &str, Writer &w) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(WriterTest, MoveAssignment) {
|
TEST(WriterTest, MoveAssignment) {
|
||||||
Writer w;
|
MemoryWriter w;
|
||||||
w << "test";
|
w << "test";
|
||||||
CheckMoveAssignWriter("test", w);
|
CheckMoveAssignWriter("test", w);
|
||||||
// This fills the inline buffer, but doesn't cause dynamic allocation.
|
// 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
|
// Adding one more character causes the content to move from the inline to
|
||||||
// a dynamically allocated buffer.
|
// a dynamically allocated buffer.
|
||||||
w << '*';
|
w << '*';
|
||||||
Writer w2;
|
MemoryWriter w2;
|
||||||
w2 = std::move(w);
|
w2 = std::move(w);
|
||||||
// Move should rip the guts of the first writer.
|
// Move should rip the guts of the first writer.
|
||||||
EXPECT_EQ(inline_buffer_ptr, w.data());
|
EXPECT_EQ(inline_buffer_ptr, w.data());
|
||||||
@ -471,17 +197,17 @@ TEST(WriterTest, Allocator) {
|
|||||||
typedef testing::StrictMock< MockAllocator<char> > MockAllocator;
|
typedef testing::StrictMock< MockAllocator<char> > MockAllocator;
|
||||||
typedef AllocatorRef<MockAllocator> TestAllocator;
|
typedef AllocatorRef<MockAllocator> TestAllocator;
|
||||||
MockAllocator alloc;
|
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::size_t size = 1.5 * fmt::internal::INLINE_BUFFER_SIZE;
|
||||||
std::vector<char> mem(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)
|
for (int i = 0; i < fmt::internal::INLINE_BUFFER_SIZE + 1; ++i)
|
||||||
w << '*';
|
w << '*';
|
||||||
EXPECT_CALL(alloc, deallocate(&mem[0], size));
|
EXPECT_CALL(alloc, deallocate(&mem[0], size));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(WriterTest, Data) {
|
TEST(WriterTest, Data) {
|
||||||
Writer w;
|
MemoryWriter w;
|
||||||
w << 42;
|
w << 42;
|
||||||
EXPECT_EQ("42", std::string(w.data(), w.size()));
|
EXPECT_EQ("42", std::string(w.data(), w.size()));
|
||||||
}
|
}
|
||||||
@ -527,13 +253,13 @@ TEST(WriterTest, WriteLongDouble) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(WriterTest, WriteDoubleAtBufferBoundary) {
|
TEST(WriterTest, WriteDoubleAtBufferBoundary) {
|
||||||
fmt::Writer writer;
|
MemoryWriter writer;
|
||||||
for (int i = 0; i < 100; ++i)
|
for (int i = 0; i < 100; ++i)
|
||||||
writer << 1.23456789;
|
writer << 1.23456789;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(WriterTest, WriteDoubleWithFilledBuffer) {
|
TEST(WriterTest, WriteDoubleWithFilledBuffer) {
|
||||||
fmt::Writer writer;
|
MemoryWriter writer;
|
||||||
// Fill the buffer.
|
// Fill the buffer.
|
||||||
for (int i = 0; i < fmt::internal::INLINE_BUFFER_SIZE; ++i)
|
for (int i = 0; i < fmt::internal::INLINE_BUFFER_SIZE; ++i)
|
||||||
writer << ' ';
|
writer << ' ';
|
||||||
@ -552,36 +278,36 @@ TEST(WriterTest, WriteWideChar) {
|
|||||||
TEST(WriterTest, WriteString) {
|
TEST(WriterTest, WriteString) {
|
||||||
CHECK_WRITE_CHAR("abc");
|
CHECK_WRITE_CHAR("abc");
|
||||||
// The following line shouldn't compile:
|
// The following line shouldn't compile:
|
||||||
//fmt::Writer() << L"abc";
|
//MemoryWriter() << L"abc";
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(WriterTest, WriteWideString) {
|
TEST(WriterTest, WriteWideString) {
|
||||||
CHECK_WRITE_WCHAR(L"abc");
|
CHECK_WRITE_WCHAR(L"abc");
|
||||||
// The following line shouldn't compile:
|
// The following line shouldn't compile:
|
||||||
//fmt::WWriter() << "abc";
|
//fmt::WMemoryWriter() << "abc";
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(WriterTest, bin) {
|
TEST(WriterTest, bin) {
|
||||||
using fmt::bin;
|
using fmt::bin;
|
||||||
EXPECT_EQ("1100101011111110", (Writer() << bin(0xcafe)).str());
|
EXPECT_EQ("1100101011111110", (MemoryWriter() << bin(0xcafe)).str());
|
||||||
EXPECT_EQ("1011101010111110", (Writer() << bin(0xbabeu)).str());
|
EXPECT_EQ("1011101010111110", (MemoryWriter() << bin(0xbabeu)).str());
|
||||||
EXPECT_EQ("1101111010101101", (Writer() << bin(0xdeadl)).str());
|
EXPECT_EQ("1101111010101101", (MemoryWriter() << bin(0xdeadl)).str());
|
||||||
EXPECT_EQ("1011111011101111", (Writer() << bin(0xbeeful)).str());
|
EXPECT_EQ("1011111011101111", (MemoryWriter() << bin(0xbeeful)).str());
|
||||||
EXPECT_EQ("11001010111111101011101010111110",
|
EXPECT_EQ("11001010111111101011101010111110",
|
||||||
(Writer() << bin(0xcafebabell)).str());
|
(MemoryWriter() << bin(0xcafebabell)).str());
|
||||||
EXPECT_EQ("11011110101011011011111011101111",
|
EXPECT_EQ("11011110101011011011111011101111",
|
||||||
(Writer() << bin(0xdeadbeefull)).str());
|
(MemoryWriter() << bin(0xdeadbeefull)).str());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(WriterTest, oct) {
|
TEST(WriterTest, oct) {
|
||||||
using fmt::oct;
|
using fmt::oct;
|
||||||
EXPECT_EQ("12", (Writer() << oct(static_cast<short>(012))).str());
|
EXPECT_EQ("12", (MemoryWriter() << oct(static_cast<short>(012))).str());
|
||||||
EXPECT_EQ("12", (Writer() << oct(012)).str());
|
EXPECT_EQ("12", (MemoryWriter() << oct(012)).str());
|
||||||
EXPECT_EQ("34", (Writer() << oct(034u)).str());
|
EXPECT_EQ("34", (MemoryWriter() << oct(034u)).str());
|
||||||
EXPECT_EQ("56", (Writer() << oct(056l)).str());
|
EXPECT_EQ("56", (MemoryWriter() << oct(056l)).str());
|
||||||
EXPECT_EQ("70", (Writer() << oct(070ul)).str());
|
EXPECT_EQ("70", (MemoryWriter() << oct(070ul)).str());
|
||||||
EXPECT_EQ("1234", (Writer() << oct(01234ll)).str());
|
EXPECT_EQ("1234", (MemoryWriter() << oct(01234ll)).str());
|
||||||
EXPECT_EQ("5670", (Writer() << oct(05670ull)).str());
|
EXPECT_EQ("5670", (MemoryWriter() << oct(05670ull)).str());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(WriterTest, hex) {
|
TEST(WriterTest, hex) {
|
||||||
@ -591,22 +317,22 @@ TEST(WriterTest, hex) {
|
|||||||
// This shouldn't compile:
|
// This shouldn't compile:
|
||||||
//fmt::IntFormatSpec<short, fmt::TypeSpec<'x'> > (*phex2)(short value) = hex;
|
//fmt::IntFormatSpec<short, fmt::TypeSpec<'x'> > (*phex2)(short value) = hex;
|
||||||
|
|
||||||
EXPECT_EQ("cafe", (Writer() << hex(0xcafe)).str());
|
EXPECT_EQ("cafe", (MemoryWriter() << hex(0xcafe)).str());
|
||||||
EXPECT_EQ("babe", (Writer() << hex(0xbabeu)).str());
|
EXPECT_EQ("babe", (MemoryWriter() << hex(0xbabeu)).str());
|
||||||
EXPECT_EQ("dead", (Writer() << hex(0xdeadl)).str());
|
EXPECT_EQ("dead", (MemoryWriter() << hex(0xdeadl)).str());
|
||||||
EXPECT_EQ("beef", (Writer() << hex(0xbeeful)).str());
|
EXPECT_EQ("beef", (MemoryWriter() << hex(0xbeeful)).str());
|
||||||
EXPECT_EQ("cafebabe", (Writer() << hex(0xcafebabell)).str());
|
EXPECT_EQ("cafebabe", (MemoryWriter() << hex(0xcafebabell)).str());
|
||||||
EXPECT_EQ("deadbeef", (Writer() << hex(0xdeadbeefull)).str());
|
EXPECT_EQ("deadbeef", (MemoryWriter() << hex(0xdeadbeefull)).str());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(WriterTest, hexu) {
|
TEST(WriterTest, hexu) {
|
||||||
using fmt::hexu;
|
using fmt::hexu;
|
||||||
EXPECT_EQ("CAFE", (Writer() << hexu(0xcafe)).str());
|
EXPECT_EQ("CAFE", (MemoryWriter() << hexu(0xcafe)).str());
|
||||||
EXPECT_EQ("BABE", (Writer() << hexu(0xbabeu)).str());
|
EXPECT_EQ("BABE", (MemoryWriter() << hexu(0xbabeu)).str());
|
||||||
EXPECT_EQ("DEAD", (Writer() << hexu(0xdeadl)).str());
|
EXPECT_EQ("DEAD", (MemoryWriter() << hexu(0xdeadl)).str());
|
||||||
EXPECT_EQ("BEEF", (Writer() << hexu(0xbeeful)).str());
|
EXPECT_EQ("BEEF", (MemoryWriter() << hexu(0xbeeful)).str());
|
||||||
EXPECT_EQ("CAFEBABE", (Writer() << hexu(0xcafebabell)).str());
|
EXPECT_EQ("CAFEBABE", (MemoryWriter() << hexu(0xcafebabell)).str());
|
||||||
EXPECT_EQ("DEADBEEF", (Writer() << hexu(0xdeadbeefull)).str());
|
EXPECT_EQ("DEADBEEF", (MemoryWriter() << hexu(0xdeadbeefull)).str());
|
||||||
}
|
}
|
||||||
|
|
||||||
class Date {
|
class Date {
|
||||||
@ -647,21 +373,21 @@ ISO8601DateFormatter iso8601(const Date &d) { return ISO8601DateFormatter(d); }
|
|||||||
|
|
||||||
TEST(WriterTest, pad) {
|
TEST(WriterTest, pad) {
|
||||||
using fmt::hex;
|
using fmt::hex;
|
||||||
EXPECT_EQ(" cafe", (Writer() << pad(hex(0xcafe), 8)).str());
|
EXPECT_EQ(" cafe", (MemoryWriter() << pad(hex(0xcafe), 8)).str());
|
||||||
EXPECT_EQ(" babe", (Writer() << pad(hex(0xbabeu), 8)).str());
|
EXPECT_EQ(" babe", (MemoryWriter() << pad(hex(0xbabeu), 8)).str());
|
||||||
EXPECT_EQ(" dead", (Writer() << pad(hex(0xdeadl), 8)).str());
|
EXPECT_EQ(" dead", (MemoryWriter() << pad(hex(0xdeadl), 8)).str());
|
||||||
EXPECT_EQ(" beef", (Writer() << pad(hex(0xbeeful), 8)).str());
|
EXPECT_EQ(" beef", (MemoryWriter() << pad(hex(0xbeeful), 8)).str());
|
||||||
EXPECT_EQ(" dead", (Writer() << pad(hex(0xdeadll), 8)).str());
|
EXPECT_EQ(" dead", (MemoryWriter() << pad(hex(0xdeadll), 8)).str());
|
||||||
EXPECT_EQ(" beef", (Writer() << pad(hex(0xbeefull), 8)).str());
|
EXPECT_EQ(" beef", (MemoryWriter() << pad(hex(0xbeefull), 8)).str());
|
||||||
|
|
||||||
EXPECT_EQ(" 11", (Writer() << pad(11, 7)).str());
|
EXPECT_EQ(" 11", (MemoryWriter() << pad(11, 7)).str());
|
||||||
EXPECT_EQ(" 22", (Writer() << pad(22u, 7)).str());
|
EXPECT_EQ(" 22", (MemoryWriter() << pad(22u, 7)).str());
|
||||||
EXPECT_EQ(" 33", (Writer() << pad(33l, 7)).str());
|
EXPECT_EQ(" 33", (MemoryWriter() << pad(33l, 7)).str());
|
||||||
EXPECT_EQ(" 44", (Writer() << pad(44ul, 7)).str());
|
EXPECT_EQ(" 44", (MemoryWriter() << pad(44ul, 7)).str());
|
||||||
EXPECT_EQ(" 33", (Writer() << pad(33ll, 7)).str());
|
EXPECT_EQ(" 33", (MemoryWriter() << pad(33ll, 7)).str());
|
||||||
EXPECT_EQ(" 44", (Writer() << pad(44ull, 7)).str());
|
EXPECT_EQ(" 44", (MemoryWriter() << pad(44ull, 7)).str());
|
||||||
|
|
||||||
Writer w;
|
MemoryWriter w;
|
||||||
w.clear();
|
w.clear();
|
||||||
w << pad(42, 5, '0');
|
w << pad(42, 5, '0');
|
||||||
EXPECT_EQ("00042", w.str());
|
EXPECT_EQ("00042", w.str());
|
||||||
@ -674,25 +400,25 @@ TEST(WriterTest, pad) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(WriterTest, PadString) {
|
TEST(WriterTest, PadString) {
|
||||||
EXPECT_EQ("test ", (Writer() << pad("test", 8)).str());
|
EXPECT_EQ("test ", (MemoryWriter() << pad("test", 8)).str());
|
||||||
EXPECT_EQ("test******", (Writer() << pad("test", 10, '*')).str());
|
EXPECT_EQ("test******", (MemoryWriter() << pad("test", 10, '*')).str());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(WriterTest, PadWString) {
|
TEST(WriterTest, PadWString) {
|
||||||
EXPECT_EQ(L"test ", (WWriter() << pad(L"test", 8)).str());
|
EXPECT_EQ(L"test ", (WMemoryWriter() << pad(L"test", 8)).str());
|
||||||
EXPECT_EQ(L"test******", (WWriter() << pad(L"test", 10, '*')).str());
|
EXPECT_EQ(L"test******", (WMemoryWriter() << pad(L"test", 10, '*')).str());
|
||||||
EXPECT_EQ(L"test******", (WWriter() << pad(L"test", 10, L'*')).str());
|
EXPECT_EQ(L"test******", (WMemoryWriter() << pad(L"test", 10, L'*')).str());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(WriterTest, NoConflictWithIOManip) {
|
TEST(WriterTest, NoConflictWithIOManip) {
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace fmt;
|
using namespace fmt;
|
||||||
EXPECT_EQ("cafe", (Writer() << hex(0xcafe)).str());
|
EXPECT_EQ("cafe", (MemoryWriter() << hex(0xcafe)).str());
|
||||||
EXPECT_EQ("12", (Writer() << oct(012)).str());
|
EXPECT_EQ("12", (MemoryWriter() << oct(012)).str());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(WriterTest, Format) {
|
TEST(WriterTest, Format) {
|
||||||
Writer w;
|
MemoryWriter w;
|
||||||
w.write("part{0}", 1);
|
w.write("part{0}", 1);
|
||||||
EXPECT_EQ(strlen("part1"), w.size());
|
EXPECT_EQ(strlen("part1"), w.size());
|
||||||
EXPECT_STREQ("part1", w.c_str());
|
EXPECT_STREQ("part1", w.c_str());
|
||||||
@ -706,7 +432,7 @@ TEST(WriterTest, Format) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(WriterTest, WWriter) {
|
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) {
|
TEST(FormatterTest, Escape) {
|
||||||
@ -1481,7 +1207,7 @@ TEST(FormatterTest, FormatStringFromSpeedTest) {
|
|||||||
|
|
||||||
TEST(FormatterTest, FormatExamples) {
|
TEST(FormatterTest, FormatExamples) {
|
||||||
using fmt::hex;
|
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);
|
std::string message = format("The answer is {}", 42);
|
||||||
EXPECT_EQ("The answer is 42", message);
|
EXPECT_EQ("The answer is 42", message);
|
||||||
@ -1489,13 +1215,13 @@ TEST(FormatterTest, FormatExamples) {
|
|||||||
EXPECT_EQ("42", format("{}", 42));
|
EXPECT_EQ("42", format("{}", 42));
|
||||||
EXPECT_EQ("42", format(std::string("{}"), 42));
|
EXPECT_EQ("42", format(std::string("{}"), 42));
|
||||||
|
|
||||||
Writer out;
|
MemoryWriter out;
|
||||||
out << "The answer is " << 42 << "\n";
|
out << "The answer is " << 42 << "\n";
|
||||||
out.write("({:+f}, {:+f})", -3.14, 3.14);
|
out.write("({:+f}, {:+f})", -3.14, 3.14);
|
||||||
EXPECT_EQ("The answer is 42\n(-3.140000, +3.140000)", out.str());
|
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++)
|
for (int i = 0; i < 10; i++)
|
||||||
writer.write("{}", i);
|
writer.write("{}", i);
|
||||||
std::string s = writer.str(); // s == 0123456789
|
std::string s = writer.str(); // s == 0123456789
|
||||||
@ -1655,7 +1381,7 @@ TEST(StrTest, Convert) {
|
|||||||
|
|
||||||
std::string format_message(int id, const char *format,
|
std::string format_message(int id, const char *format,
|
||||||
const fmt::ArgList &args) {
|
const fmt::ArgList &args) {
|
||||||
fmt::Writer w;
|
MemoryWriter w;
|
||||||
w.write("[{}] ", id);
|
w.write("[{}] ", id);
|
||||||
w.write(format, args);
|
w.write(format, args);
|
||||||
return w.str();
|
return w.str();
|
||||||
|
@ -348,7 +348,7 @@ TEST(StreamingAssertionsTest, EXPECT_WRITE) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(UtilTest, FormatSystemError) {
|
TEST(UtilTest, FormatSystemError) {
|
||||||
fmt::Writer out;
|
fmt::MemoryWriter out;
|
||||||
fmt::internal::format_system_error(out, EDOM, "test message");
|
fmt::internal::format_system_error(out, EDOM, "test message");
|
||||||
EXPECT_EQ(out.str(), format_system_error(EDOM, "test message"));
|
EXPECT_EQ(out.str(), format_system_error(EDOM, "test message"));
|
||||||
}
|
}
|
||||||
|
@ -92,7 +92,7 @@ std::string OutputRedirect::restore_and_read() {
|
|||||||
#endif // FMT_USE_FILE_DESCRIPTORS
|
#endif // FMT_USE_FILE_DESCRIPTORS
|
||||||
|
|
||||||
std::string format_system_error(int error_code, fmt::StringRef message) {
|
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);
|
fmt::internal::format_system_error(out, error_code, message);
|
||||||
return out.str();
|
return out.str();
|
||||||
}
|
}
|
||||||
|
@ -25,8 +25,8 @@
|
|||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef FMT_GTEST_EXTRA_H
|
#ifndef FMT_GTEST_EXTRA_H_
|
||||||
#define FMT_GTEST_EXTRA_H
|
#define FMT_GTEST_EXTRA_H_
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
@ -135,4 +135,4 @@ class OutputRedirect {
|
|||||||
|
|
||||||
#endif // FMT_USE_FILE_DESCRIPTORS
|
#endif // FMT_USE_FILE_DESCRIPTORS
|
||||||
|
|
||||||
#endif // FMT_GTEST_EXTRA_H
|
#endif // FMT_GTEST_EXTRA_H_
|
||||||
|
83
test/mock-allocator.h
Normal file
83
test/mock-allocator.h
Normal 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_
|
@ -29,7 +29,14 @@
|
|||||||
#include <climits>
|
#include <climits>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
|
#if FMT_USE_TYPE_TRAITS
|
||||||
|
# include <type_traits>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "gmock/gmock.h"
|
||||||
#include "gtest-extra.h"
|
#include "gtest-extra.h"
|
||||||
|
#include "mock-allocator.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
// Check if format.h compiles with windows.h included.
|
// Check if format.h compiles with windows.h included.
|
||||||
@ -42,8 +49,11 @@
|
|||||||
#undef max
|
#undef max
|
||||||
|
|
||||||
using fmt::StringRef;
|
using fmt::StringRef;
|
||||||
using fmt::internal::Value;
|
|
||||||
using fmt::internal::Arg;
|
using fmt::internal::Arg;
|
||||||
|
using fmt::internal::Value;
|
||||||
|
using fmt::internal::MemoryBuffer;
|
||||||
|
|
||||||
|
using testing::Return;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@ -62,9 +72,241 @@ Arg make_arg(const T &value) {
|
|||||||
fmt::internal::MakeValue<Char>::type(value));
|
fmt::internal::MakeValue<Char>::type(value));
|
||||||
return arg;
|
return arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // 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) {
|
TEST(UtilTest, Increment) {
|
||||||
char s[10] = "123";
|
char s[10] = "123";
|
||||||
increment(s);
|
increment(s);
|
||||||
@ -236,7 +478,7 @@ TEST(ArgTest, MakeArg) {
|
|||||||
Arg arg = make_arg<char>(t);
|
Arg arg = make_arg<char>(t);
|
||||||
EXPECT_EQ(fmt::internal::Arg::CUSTOM, arg.type);
|
EXPECT_EQ(fmt::internal::Arg::CUSTOM, arg.type);
|
||||||
EXPECT_EQ(&t, arg.custom.value);
|
EXPECT_EQ(&t, arg.custom.value);
|
||||||
fmt::Writer w;
|
fmt::MemoryWriter w;
|
||||||
fmt::BasicFormatter<char> formatter(w);
|
fmt::BasicFormatter<char> formatter(w);
|
||||||
const char *s = "}";
|
const char *s = "}";
|
||||||
arg.custom.format(&formatter, &t, &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) {
|
} catch (const fmt::SystemError &e) {
|
||||||
error = e;
|
error = e;
|
||||||
}
|
}
|
||||||
fmt::Writer message;
|
fmt::MemoryWriter message;
|
||||||
format(message, error_code, "test error");
|
format(message, error_code, "test error");
|
||||||
EXPECT_EQ(message.str(), error.what());
|
EXPECT_EQ(message.str(), error.what());
|
||||||
EXPECT_EQ(error_code, error.error_code());
|
EXPECT_EQ(error_code, error.error_code());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(UtilTest, FormatSystemError) {
|
TEST(UtilTest, FormatSystemError) {
|
||||||
fmt::Writer message;
|
fmt::MemoryWriter message;
|
||||||
fmt::internal::format_system_error(message, EDOM, "test");
|
fmt::internal::format_system_error(message, EDOM, "test");
|
||||||
EXPECT_EQ(fmt::format("test: {}", get_system_error(EDOM)), message.str());
|
EXPECT_EQ(fmt::format("test: {}", get_system_error(EDOM)), message.str());
|
||||||
message.clear();
|
message.clear();
|
||||||
@ -459,7 +701,7 @@ TEST(UtilTest, SystemError) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(UtilTest, ReportSystemError) {
|
TEST(UtilTest, ReportSystemError) {
|
||||||
fmt::Writer out;
|
fmt::MemoryWriter out;
|
||||||
fmt::internal::format_system_error(out, EDOM, "test error");
|
fmt::internal::format_system_error(out, EDOM, "test error");
|
||||||
out << '\n';
|
out << '\n';
|
||||||
EXPECT_WRITE(stderr, fmt::report_system_error(EDOM, "test error"), out.str());
|
EXPECT_WRITE(stderr, fmt::report_system_error(EDOM, "test error"), out.str());
|
||||||
@ -501,3 +743,5 @@ TEST(UtilTest, ReportWindowsError) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
|
|
||||||
|
// TODO: test Buffer
|
||||||
|
Loading…
Reference in New Issue
Block a user