Commit ad611c13 authored by Kenton Varda's avatar Kenton Varda

Improve String.

parent 4958d3a4
......@@ -176,7 +176,7 @@ kj::String stringify(DynamicValue::Reader value) {
std::stringstream out;
print(out, value, schema::Type::Body::STRUCT_TYPE);
auto content = out.str();
return kj::String(content.data(), content.size());
return kj::heapString(content.data(), content.size());
}
namespace internal {
......
......@@ -283,5 +283,24 @@ TEST(Array, AraryBuilderAddAll) {
EXPECT_EQ(0, TestObject::copiedCount);
}
TEST(Array, HeapCopy) {
{
Array<char> copy = heapArray("foo", 3);
EXPECT_EQ(3u, copy.size());
EXPECT_EQ("foo", std::string(copy.begin(), 3));
}
{
Array<char> copy = heapArray(ArrayPtr<const char>("bar", 3));
EXPECT_EQ(3u, copy.size());
EXPECT_EQ("bar", std::string(copy.begin(), 3));
}
{
const char* ptr = "baz";
Array<char> copy = heapArray<char>(ptr, ptr + 3);
EXPECT_EQ(3u, copy.size());
EXPECT_EQ("baz", std::string(copy.begin(), 3));
}
}
} // namespace
} // namespace kj
......@@ -156,14 +156,6 @@ namespace internal {
class HeapArrayDisposer final: public ArrayDisposer {
public:
static void* allocateImpl(size_t elementSize, size_t elementCount, size_t capacity,
void (*constructElement)(void*), void (*destroyElement)(void*));
// Allocates and constructs the array. Both function pointers are null if the constructor is
// trivial, otherwise destroyElement is null if the constructor doesn't throw.
virtual void disposeImpl(void* firstElement, size_t elementSize, size_t elementCount,
size_t capacity, void (*destroyElement)(void*)) const override;
template <typename T>
static T* allocate(size_t count);
template <typename T>
......@@ -172,6 +164,14 @@ public:
static const HeapArrayDisposer instance;
private:
static void* allocateImpl(size_t elementSize, size_t elementCount, size_t capacity,
void (*constructElement)(void*), void (*destroyElement)(void*));
// Allocates and constructs the array. Both function pointers are null if the constructor is
// trivial, otherwise destroyElement is null if the constructor doesn't throw.
virtual void disposeImpl(void* firstElement, size_t elementSize, size_t elementCount,
size_t capacity, void (*destroyElement)(void*)) const override;
template <typename T, bool hasTrivialConstructor = __has_trivial_constructor(T),
bool hasNothrowConstructor = __has_nothrow_constructor(T)>
struct Allocate_;
......@@ -189,6 +189,11 @@ inline Array<T> heapArray(size_t size) {
internal::HeapArrayDisposer::instance);
}
template <typename T> Array<T> heapArray(const T* content, size_t size);
template <typename T> Array<T> heapArray(ArrayPtr<const T> content);
template <typename T, typename Iterator> Array<T> heapArray(Iterator begin, Iterator end);
// Allocate a heap arary containing a copy of the given content.
// =======================================================================================
// ArrayBuilder
......@@ -462,6 +467,27 @@ void ArrayBuilder<T>::addAll(Iterator start, Iterator end) {
pos = internal::copyConstructArray(pos, start, end);
}
template <typename T>
Array<T> heapArray(const T* content, size_t size) {
ArrayBuilder<T> builder = heapArrayBuilder<T>(size);
builder.addAll(content, content + size);
return builder.finish();
}
template <typename T>
Array<T> heapArray(ArrayPtr<const T> content) {
ArrayBuilder<T> builder = heapArrayBuilder<T>(content.size());
builder.addAll(content);
return builder.finish();
}
template <typename T, typename Iterator> Array<T>
heapArray(Iterator begin, Iterator end) {
ArrayBuilder<T> builder = heapArrayBuilder<T>(end - begin);
builder.addAll(begin, end);
return builder.finish();
}
} // namespace kj
#endif // KJ_ARRAY_H_
......@@ -181,6 +181,14 @@ template <typename T> struct IsLvalueReference_<T&> { static constexpr bool valu
template <typename T>
inline constexpr bool isLvalueReference() { return IsLvalueReference_<T>::value; }
template <typename T> struct Decay_ { typedef T Type; };
template <typename T> struct Decay_<T&> { typedef typename Decay_<T>::Type Type; };
template <typename T> struct Decay_<T&&> { typedef typename Decay_<T>::Type Type; };
template <typename T> struct Decay_<T[]> { typedef typename Decay_<T*>::Type Type; };
template <typename T> struct Decay_<const T> { typedef typename Decay_<T>::Type Type; };
template <typename T> struct Decay_<volatile T> { typedef typename Decay_<T>::Type Type; };
template <typename T> using Decay = typename Decay_<T>::Type;
template <typename T>
T instance() noexcept;
// Like std::declval, but doesn't transform T into an rvalue reference. If you want that, specify
......@@ -494,7 +502,7 @@ public:
inline T& front() const { return *ptr; }
inline T& back() const { return *(ptr + size_ - 1); }
inline ArrayPtr slice(size_t start, size_t end) {
inline ArrayPtr slice(size_t start, size_t end) const {
KJ_INLINE_DPRECOND(start <= end && end <= size_, "Out-of-bounds ArrayPtr::slice().");
return ArrayPtr(ptr + start, end - start);
}
......
......@@ -21,7 +21,6 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define KJ_PRIVATE
#include "exception.h"
#include "util.h"
#include "logging.h"
......@@ -56,7 +55,7 @@ ArrayPtr<const char> operator*(const Stringifier&, Exception::Durability durabil
}
Exception::Exception(Nature nature, Durability durability, const char* file, int line,
Array<char> description) noexcept
String description) noexcept
: file(file), line(line), nature(nature), durability(durability),
description(mv(description)) {
traceCount = backtrace(trace, 16);
......@@ -68,7 +67,7 @@ Exception::Exception(const Exception& other) noexcept
memcpy(trace, other.trace, sizeof(trace[0]) * traceCount);
KJ_IF_MAYBE(c, other.context) {
context = heap<Context>(**c);
context = heap(**c);
}
}
......@@ -77,11 +76,11 @@ Exception::~Exception() noexcept {}
Exception::Context::Context(const Context& other) noexcept
: file(other.file), line(other.line), description(str(other.description)) {
KJ_IF_MAYBE(n, other.next) {
next = heap<Context>(**n);
next = heap(**n);
}
}
void Exception::wrapContext(const char* file, int line, Array<char>&& description) {
void Exception::wrapContext(const char* file, int line, String&& description) {
context = heap<Context>(file, line, mv(description), mv(context));
}
......@@ -98,7 +97,7 @@ const char* Exception::what() const noexcept {
}
}
Array<Array<char>> contextText = heapArray<Array<char>>(contextDepth);
Array<String> contextText = heapArray<String>(contextDepth);
contextDepth = 0;
contextPtr = &context;
......@@ -118,7 +117,7 @@ const char* Exception::what() const noexcept {
file, ":", line, ": ", nature,
durability == Durability::TEMPORARY ? " (temporary)" : "",
this->description == nullptr ? "" : ": ", this->description,
"\nstack: ", strArray(arrayPtr(trace, traceCount), " "), '\0');
"\nstack: ", strArray(arrayPtr(trace, traceCount), " "));
return whatBuffer.begin();
}
......@@ -163,14 +162,14 @@ void ExceptionCallback::onFatalException(Exception&& exception) {
#endif
}
void ExceptionCallback::logMessage(ArrayPtr<const char> text) {
void ExceptionCallback::logMessage(StringPtr text) {
while (text != nullptr) {
ssize_t n = write(STDERR_FILENO, text.begin(), text.size());
if (n <= 0) {
// stderr is broken. Give up.
return;
}
text = text.slice(n, text.size());
text = text.slice(n);
}
}
......
......@@ -27,6 +27,7 @@
#include <exception>
#include "memory.h"
#include "array.h"
#include "string.h"
namespace kj {
......@@ -64,7 +65,7 @@ public:
};
Exception(Nature nature, Durability durability, const char* file, int line,
Array<char> description = nullptr) noexcept;
String description = nullptr) noexcept;
Exception(const Exception& other) noexcept;
Exception(Exception&& other) = default;
~Exception() noexcept;
......@@ -80,10 +81,10 @@ public:
const char* file;
int line;
Array<char> description;
String description;
Maybe<Own<Context>> next;
Context(const char* file, int line, Array<char>&& description, Maybe<Own<Context>>&& next)
Context(const char* file, int line, String&& description, Maybe<Own<Context>>&& next)
: file(file), line(line), description(mv(description)), next(mv(next)) {}
Context(const Context& other) noexcept;
};
......@@ -96,7 +97,7 @@ public:
}
}
void wrapContext(const char* file, int line, Array<char>&& description);
void wrapContext(const char* file, int line, String&& description);
// Wraps the context in a new node. This becomes the head node returned by getContext() -- it
// is expected that contexts will be added in reverse order as the exception passes up the
// callback stack.
......@@ -108,11 +109,11 @@ private:
int line;
Nature nature;
Durability durability;
Array<char> description;
String description;
Maybe<Own<Context>> context;
void* trace[16];
uint traceCount;
mutable Array<char> whatBuffer;
mutable String whatBuffer;
};
struct Stringifier;
......@@ -143,7 +144,7 @@ public:
// aborting. The default implementation throws an exception unless the library was compiled with
// -fno-exceptions, in which case it logs an error and returns.
virtual void logMessage(ArrayPtr<const char> text);
virtual void logMessage(StringPtr text);
// Called when something wants to log some debug text. The text always ends in a newline if
// it is non-empty. The default implementation writes the text to stderr.
......
......@@ -21,7 +21,6 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define KJ_PRIVATE
#include "logging.h"
#include "exception.h"
#include <gtest/gtest.h>
......@@ -70,7 +69,7 @@ public:
throw MockException();
}
void logMessage(ArrayPtr<const char> text) override {
void logMessage(StringPtr text) override {
this->text += "log message: ";
this->text.append(text.begin(), text.end());
}
......
......@@ -21,7 +21,6 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define KJ_PRIVATE
#include "logging.h"
#include <stdlib.h>
#include <ctype.h>
......@@ -52,8 +51,8 @@ enum DescriptionStyle {
SYSCALL
};
static Array<char> makeDescription(DescriptionStyle style, const char* code, int errorNumber,
const char* macroArgs, ArrayPtr<Array<char>> argValues) {
static String makeDescription(DescriptionStyle style, const char* code, int errorNumber,
const char* macroArgs, ArrayPtr<String> argValues) {
KJ_STACK_ARRAY(ArrayPtr<const char>, argNames, argValues.size(), 8, 64);
if (argValues.size() > 0) {
......@@ -112,22 +111,22 @@ static Array<char> makeDescription(DescriptionStyle style, const char* code, int
}
{
ArrayPtr<const char> expected = stringPtr("expected ");
ArrayPtr<const char> codeArray = style == LOG ? nullptr : stringPtr(code);
ArrayPtr<const char> sep = stringPtr(" = ");
ArrayPtr<const char> delim = stringPtr("; ");
ArrayPtr<const char> colon = stringPtr(": ");
StringPtr expected = "expected ";
StringPtr codeArray = style == LOG ? nullptr : StringPtr(code);
StringPtr sep = " = ";
StringPtr delim = "; ";
StringPtr colon = ": ";
if (style == ASSERTION && strcmp(code, "false") == 0) {
// Don't print "expected false", that's silly.
style = LOG;
}
ArrayPtr<const char> sysErrorArray;
StringPtr sysErrorArray;
#if __USE_GNU
char buffer[256];
if (style == SYSCALL) {
sysErrorArray = stringPtr(strerror_r(errorNumber, buffer, sizeof(buffer)));
sysErrorArray = strerror_r(errorNumber, buffer, sizeof(buffer));
}
#else
// TODO(port): Other unixes should have strerror_r but it may have a different signature.
......@@ -157,40 +156,37 @@ static Array<char> makeDescription(DescriptionStyle style, const char* code, int
totalSize += argValues[i].size();
}
ArrayBuilder<char> result = heapArrayBuilder<char>(totalSize);
String result = heapString(totalSize);
char* pos = result.begin();
switch (style) {
case LOG:
break;
case ASSERTION:
result.addAll(expected);
result.addAll(codeArray);
pos = fill(pos, expected, codeArray);
break;
case SYSCALL:
result.addAll(codeArray);
result.addAll(colon);
result.addAll(sysErrorArray);
pos = fill(pos, codeArray, colon, sysErrorArray);
break;
}
for (size_t i = 0; i < argValues.size(); i++) {
if (i > 0 || style != LOG) {
result.addAll(delim);
pos = fill(pos, delim);
}
if (argNames[i].size() > 0 && argNames[i][0] != '\"') {
result.addAll(argNames[i]);
result.addAll(sep);
pos = fill(pos, argNames[i], sep);
}
result.addAll(argValues[i]);
pos = fill(pos, argValues[i]);
}
return result.finish();
return result;
}
}
} // namespace
void Log::logInternal(const char* file, int line, Severity severity, const char* macroArgs,
ArrayPtr<Array<char>> argValues) {
ArrayPtr<String> argValues) {
getExceptionCallback().logMessage(
str(severity, ": ", file, ":", line, ": ",
makeDescription(LOG, nullptr, 0, macroArgs, argValues), '\n'));
......@@ -198,7 +194,7 @@ void Log::logInternal(const char* file, int line, Severity severity, const char*
void Log::recoverableFaultInternal(
const char* file, int line, Exception::Nature nature,
const char* condition, const char* macroArgs, ArrayPtr<Array<char>> argValues) {
const char* condition, const char* macroArgs, ArrayPtr<String> argValues) {
getExceptionCallback().onRecoverableException(
Exception(nature, Exception::Durability::PERMANENT, file, line,
makeDescription(ASSERTION, condition, 0, macroArgs, argValues)));
......@@ -206,7 +202,7 @@ void Log::recoverableFaultInternal(
void Log::fatalFaultInternal(
const char* file, int line, Exception::Nature nature,
const char* condition, const char* macroArgs, ArrayPtr<Array<char>> argValues) {
const char* condition, const char* macroArgs, ArrayPtr<String> argValues) {
getExceptionCallback().onFatalException(
Exception(nature, Exception::Durability::PERMANENT, file, line,
makeDescription(ASSERTION, condition, 0, macroArgs, argValues)));
......@@ -215,7 +211,7 @@ void Log::fatalFaultInternal(
void Log::recoverableFailedSyscallInternal(
const char* file, int line, const char* call,
int errorNumber, const char* macroArgs, ArrayPtr<Array<char>> argValues) {
int errorNumber, const char* macroArgs, ArrayPtr<String> argValues) {
getExceptionCallback().onRecoverableException(
Exception(Exception::Nature::OS_ERROR, Exception::Durability::PERMANENT, file, line,
makeDescription(SYSCALL, call, errorNumber, macroArgs, argValues)));
......@@ -223,7 +219,7 @@ void Log::recoverableFailedSyscallInternal(
void Log::fatalFailedSyscallInternal(
const char* file, int line, const char* call,
int errorNumber, const char* macroArgs, ArrayPtr<Array<char>> argValues) {
int errorNumber, const char* macroArgs, ArrayPtr<String> argValues) {
getExceptionCallback().onFatalException(
Exception(Exception::Nature::OS_ERROR, Exception::Durability::PERMANENT, file, line,
makeDescription(SYSCALL, call, errorNumber, macroArgs, argValues)));
......@@ -231,7 +227,7 @@ void Log::fatalFailedSyscallInternal(
}
void Log::addContextToInternal(Exception& exception, const char* file, int line,
const char* macroArgs, ArrayPtr<Array<char>> argValues) {
const char* macroArgs, ArrayPtr<String> argValues) {
exception.wrapContext(file, line, makeDescription(LOG, nullptr, 0, macroArgs, argValues));
}
......@@ -251,7 +247,7 @@ void Log::Context::onFatalException(Exception&& exception) {
addTo(exception);
next.onFatalException(kj::mv(exception));
}
void Log::Context::logMessage(ArrayPtr<const char> text) {
void Log::Context::logMessage(StringPtr text) {
// TODO(someday): We could do something like log the context and then indent all log messages
// written until the end of the context.
next.logMessage(text);
......
......@@ -158,7 +158,7 @@ public:
virtual void onRecoverableException(Exception&& exception) override;
virtual void onFatalException(Exception&& exception) override;
virtual void logMessage(ArrayPtr<const char> text) override;
virtual void logMessage(StringPtr text) override;
private:
ExceptionCallback& next;
......@@ -186,23 +186,23 @@ private:
static Severity minSeverity;
static void logInternal(const char* file, int line, Severity severity, const char* macroArgs,
ArrayPtr<Array<char>> argValues);
ArrayPtr<String> argValues);
static void recoverableFaultInternal(
const char* file, int line, Exception::Nature nature,
const char* condition, const char* macroArgs, ArrayPtr<Array<char>> argValues);
const char* condition, const char* macroArgs, ArrayPtr<String> argValues);
static void fatalFaultInternal(
const char* file, int line, Exception::Nature nature,
const char* condition, const char* macroArgs, ArrayPtr<Array<char>> argValues)
const char* condition, const char* macroArgs, ArrayPtr<String> argValues)
KJ_NORETURN;
static void recoverableFailedSyscallInternal(
const char* file, int line, const char* call,
int errorNumber, const char* macroArgs, ArrayPtr<Array<char>> argValues);
int errorNumber, const char* macroArgs, ArrayPtr<String> argValues);
static void fatalFailedSyscallInternal(
const char* file, int line, const char* call,
int errorNumber, const char* macroArgs, ArrayPtr<Array<char>> argValues)
int errorNumber, const char* macroArgs, ArrayPtr<String> argValues)
KJ_NORETURN;
static void addContextToInternal(Exception& exception, const char* file, int line,
const char* macroArgs, ArrayPtr<Array<char>> argValues);
const char* macroArgs, ArrayPtr<String> argValues);
static int getOsErrorNumber();
// Get the error code of the last error (e.g. from errno). Returns -1 on EINTR.
......@@ -287,14 +287,14 @@ ArrayPtr<const char> operator*(const Stringifier&, Log::Severity severity);
template <typename... Params>
void Log::log(const char* file, int line, Severity severity, const char* macroArgs,
Params&&... params) {
Array<char> argValues[sizeof...(Params)] = {str(params)...};
String argValues[sizeof...(Params)] = {str(params)...};
logInternal(file, line, severity, macroArgs, arrayPtr(argValues, sizeof...(Params)));
}
template <typename... Params>
void Log::recoverableFault(const char* file, int line, Exception::Nature nature,
const char* condition, const char* macroArgs, Params&&... params) {
Array<char> argValues[sizeof...(Params)] = {str(params)...};
String argValues[sizeof...(Params)] = {str(params)...};
recoverableFaultInternal(file, line, nature, condition, macroArgs,
arrayPtr(argValues, sizeof...(Params)));
}
......@@ -302,7 +302,7 @@ void Log::recoverableFault(const char* file, int line, Exception::Nature nature,
template <typename... Params>
void Log::fatalFault(const char* file, int line, Exception::Nature nature,
const char* condition, const char* macroArgs, Params&&... params) {
Array<char> argValues[sizeof...(Params)] = {str(params)...};
String argValues[sizeof...(Params)] = {str(params)...};
fatalFaultInternal(file, line, nature, condition, macroArgs,
arrayPtr(argValues, sizeof...(Params)));
}
......@@ -315,7 +315,7 @@ bool Log::recoverableSyscall(Call&& call, const char* file, int line, const char
int errorNum = getOsErrorNumber();
// getOsErrorNumber() returns -1 to indicate EINTR
if (errorNum != -1) {
Array<char> argValues[sizeof...(Params)] = {str(params)...};
String argValues[sizeof...(Params)] = {str(params)...};
recoverableFailedSyscallInternal(file, line, callText, errorNum,
macroArgs, arrayPtr(argValues, sizeof...(Params)));
return false;
......@@ -333,7 +333,7 @@ auto Log::syscall(Call&& call, const char* file, int line, const char* callText,
int errorNum = getOsErrorNumber();
// getOsErrorNumber() returns -1 to indicate EINTR
if (errorNum != -1) {
Array<char> argValues[sizeof...(Params)] = {str(params)...};
String argValues[sizeof...(Params)] = {str(params)...};
fatalFailedSyscallInternal(file, line, callText, errorNum,
macroArgs, arrayPtr(argValues, sizeof...(Params)));
}
......@@ -346,7 +346,7 @@ template <typename... Params>
void Log::reportFailedRecoverableSyscall(
int errorNumber, const char* file, int line, const char* callText,
const char* macroArgs, Params&&... params) {
Array<char> argValues[sizeof...(Params)] = {str(params)...};
String argValues[sizeof...(Params)] = {str(params)...};
recoverableFailedSyscallInternal(file, line, callText, errorNumber, macroArgs,
arrayPtr(argValues, sizeof...(Params)));
}
......@@ -355,7 +355,7 @@ template <typename... Params>
void Log::reportFailedSyscall(
int errorNumber, const char* file, int line, const char* callText,
const char* macroArgs, Params&&... params) {
Array<char> argValues[sizeof...(Params)] = {str(params)...};
String argValues[sizeof...(Params)] = {str(params)...};
fatalFailedSyscallInternal(file, line, callText, errorNumber, macroArgs,
arrayPtr(argValues, sizeof...(Params)));
}
......@@ -363,7 +363,7 @@ void Log::reportFailedSyscall(
template <typename... Params>
void Log::addContextTo(Exception& exception, const char* file, int line,
const char* macroArgs, Params&&... params) {
Array<char> argValues[sizeof...(Params)] = {str(params)...};
String argValues[sizeof...(Params)] = {str(params)...};
addContextToInternal(exception, file, line, macroArgs, arrayPtr(argValues, sizeof...(Params)));
}
......
......@@ -156,6 +156,17 @@ Own<T> heap(Params&&... params) {
return Own<T>(new T(kj::fwd<Params>(params)...), internal::HeapDisposer<T>::instance);
}
template <typename T>
Own<Decay<T>> heap(T&& orig) {
// Allocate a copy (or move) of the argument on the heap.
//
// The purpose of this overload is to allow you to omit the template parameter as there is only
// one argument and the purpose is to copy it.
typedef Decay<T> T2;
return Own<T2>(new T2(kj::fwd<T>(orig)), internal::HeapDisposer<T2>::instance);
}
// =======================================================================================
// Inline implementation details
......
......@@ -22,16 +22,20 @@
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "string.h"
#include "logging.h"
namespace kj {
String::String(const char* value): content(heapArray<char>(strlen(value) + 1)) {
strcpy(content.begin(), value);
String heapString(size_t size) {
char* buffer = internal::HeapArrayDisposer::allocate<char>(size + 1);
buffer[size] = '\0';
return String(buffer, size, internal::HeapArrayDisposer::instance);
}
String::String(const char* value, size_t length): content(heapArray<char>(length + 1)) {
memcpy(content.begin(), value, length);
content[length] = '\0';
String heapString(const char* value, size_t size) {
char* buffer = internal::HeapArrayDisposer::allocate<char>(size + 1);
memcpy(buffer, value, size + 1);
return String(buffer, size, internal::HeapArrayDisposer::instance);
}
} // namespace kj
......@@ -29,39 +29,181 @@
namespace kj {
inline ArrayPtr<const char> stringPtr(const char* text) {
return arrayPtr(text, strlen(text));
}
class StringPtr;
class String;
// =======================================================================================
// StringPtr -- A NUL-terminated ArrayPtr<const char> containing UTF-8 text.
//
// NUL bytes are allowed to appear before the end of the string. The only requirement is that
// a NUL byte appear immediately after the last byte of the content. This terminator byte is not
// counted in the string's size.
class StringPtr {
public:
inline StringPtr(): content("", 1) {}
inline StringPtr(decltype(nullptr)): content("", 1) {}
inline StringPtr(const char* value): content(value, strlen(value) + 1) {}
inline StringPtr(const String& value);
inline operator ArrayPtr<const char>() const;
inline ArrayPtr<const char> asArray() const;
// Result does not include NUL terminator.
inline const char* cStr() const { return content.begin(); }
// Returns NUL-terminated string.
inline size_t size() const { return content.size() - 1; }
// Result does not include NUL terminator.
inline char operator[](size_t index) const { return content[index]; }
inline const char* begin() const { return content.begin(); }
inline const char* end() const { return content.end() - 1; }
inline bool operator==(decltype(nullptr)) const { return content.size() <= 1; }
inline bool operator!=(decltype(nullptr)) const { return content.size() > 1; }
inline bool operator==(StringPtr other) const;
inline bool operator!=(StringPtr other) const { return !(*this == other); }
inline StringPtr slice(size_t start) const;
inline ArrayPtr<const char> slice(size_t start, size_t end) const;
// A string slice is only NUL-terminated if it is a suffix, so slice() has a one-parameter
// version that assumes end = size().
private:
inline StringPtr(ArrayPtr<const char> content): content(content) {}
ArrayPtr<const char> content;
};
inline bool operator==(const char* a, const StringPtr& b) { return b == a; }
inline bool operator!=(const char* a, const StringPtr& b) { return b != a; }
// =======================================================================================
// String -- Just a NUL-terminated Array<char>.
// String -- A NUL-terminated Array<char> containing UTF-8 text.
//
// NUL bytes are allowed to appear before the end of the string. The only requirement is that
// a NUL byte appear immediately after the last byte of the content. This terminator byte is not
// counted in the string's size.
//
// To allocate a String, you must call kj::heapString(). We do not implement implicit copying to
// the heap because this hides potential inefficiency from the developer.
class String {
public:
String() = default;
String(const char* value);
String(const char* value, size_t length);
inline String(decltype(nullptr)): content(nullptr) {}
inline String(char* value, size_t size, const ArrayDisposer& disposer);
// Does not copy. `size` does not include NUL terminator, but `value` must be NUL-terminated.
inline operator ArrayPtr<char>();
inline operator ArrayPtr<const char>() const;
inline ArrayPtr<char> asArray();
inline ArrayPtr<const char> asArray() const;
inline const char* cStr() const { return content == nullptr ? "" : content.begin(); }
// Result does not include NUL terminator.
inline const char* cStr() const;
inline size_t size() const;
// Result does not include NUL terminator.
inline char operator[](size_t index) const;
inline char& operator[](size_t index);
inline size_t size() const { return content == nullptr ? 0 : content.size() - 1; }
inline char* begin();
inline char* end();
inline const char* begin() const;
inline const char* end() const;
inline char* begin() { return content == nullptr ? nullptr : content.begin(); }
inline char* end() { return content == nullptr ? nullptr : content.end() - 1; }
inline const char* begin() const { return content == nullptr ? nullptr : content.begin(); }
inline const char* end() const { return content == nullptr ? nullptr : content.end() - 1; }
inline bool operator==(decltype(nullptr)) const { return content.size() <= 1; }
inline bool operator!=(decltype(nullptr)) const { return content.size() > 1; }
inline bool operator==(StringPtr other) const { return StringPtr(*this) == other; }
inline bool operator!=(StringPtr other) const { return !(*this == other); }
private:
Array<char> content;
};
inline bool operator==(const char* a, const String& b) { return b == a; }
inline bool operator!=(const char* a, const String& b) { return b != a; }
String heapString(size_t size);
// Allocate a String of the given size on the heap, not including NUL terminator. The NUL
// terminator will be initialized automatically but the rest of the content is not initialized.
String heapString(const char* value);
String heapString(const char* value, size_t size);
String heapString(StringPtr value);
String heapString(ArrayPtr<const char> value);
// Allocates a copy of the given value on the heap.
// =======================================================================================
// Inline implementation details.
inline StringPtr::StringPtr(const String& value): content(value.begin(), value.size() + 1) {}
inline StringPtr::operator ArrayPtr<const char>() const {
return content.slice(0, content.size() - 1);
}
inline ArrayPtr<const char> StringPtr::asArray() const {
return content.slice(0, content.size() - 1);
}
inline bool StringPtr::operator==(StringPtr other) const {
return content.size() == other.content.size() &&
memcmp(content.begin(), other.content.begin(), content.size() - 1) == 0;
}
inline StringPtr StringPtr::slice(size_t start) const {
return StringPtr(content.slice(start, content.size()));
}
inline ArrayPtr<const char> StringPtr::slice(size_t start, size_t end) const {
return content.slice(start, end);
}
inline String::operator ArrayPtr<char>() {
return content == nullptr ? ArrayPtr<char>(nullptr) : content.slice(0, content.size() - 1);
}
inline String::operator ArrayPtr<const char>() const {
return content == nullptr ? ArrayPtr<const char>(nullptr) : content.slice(0, content.size() - 1);
}
inline ArrayPtr<char> String::asArray() {
return content == nullptr ? ArrayPtr<char>(nullptr) : content.slice(0, content.size() - 1);
}
inline ArrayPtr<const char> String::asArray() const {
return content == nullptr ? ArrayPtr<char>(nullptr) : content.slice(0, content.size() - 1);
return content == nullptr ? ArrayPtr<const char>(nullptr) : content.slice(0, content.size() - 1);
}
inline const char* String::cStr() const { return content == nullptr ? "" : content.begin(); }
inline size_t String::size() const { return content == nullptr ? 0 : content.size() - 1; }
inline char String::operator[](size_t index) const { return content[index]; }
inline char& String::operator[](size_t index) { return content[index]; }
inline char* String::begin() { return content == nullptr ? nullptr : content.begin(); }
inline char* String::end() { return content == nullptr ? nullptr : content.end() - 1; }
inline const char* String::begin() const { return content == nullptr ? nullptr : content.begin(); }
inline const char* String::end() const { return content == nullptr ? nullptr : content.end() - 1; }
inline String::String(char* value, size_t size, const ArrayDisposer& disposer)
: content(value, size + 1, disposer) {
KJ_INLINE_DPRECOND(value[size] == '\0', "String must be NUL-terminated.");
}
inline String heapString(const char* value) {
return heapString(value, strlen(value));
}
inline String heapString(StringPtr value) {
return heapString(value.begin(), value.size());
}
inline String heapString(ArrayPtr<const char> value) {
return heapString(value.begin(), value.size());
}
} // namespace kj
......
......@@ -21,7 +21,6 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define KJ_PRIVATE
#include "util.h"
#include <gtest/gtest.h>
#include <string>
......@@ -30,15 +29,11 @@ namespace kj {
namespace internal {
namespace {
std::string arrayToStr(Array<char> arr) {
return std::string(arr.begin(), arr.size());
}
TEST(Util, Foo) {
EXPECT_EQ("foobar", arrayToStr(str("foo", "bar")));
EXPECT_EQ("1 2 3 4", arrayToStr(str(1, " ", 2u, " ", 3l, " ", 4ll)));
EXPECT_EQ("1.5 foo 1e15 bar -3", arrayToStr(str(1.5f, " foo ", 1e15, " bar ", -3)));
EXPECT_EQ("foo", arrayToStr(str('f', 'o', 'o')));
EXPECT_EQ("foobar", str("foo", "bar"));
EXPECT_EQ("1 2 3 4", str(1, " ", 2u, " ", 3l, " ", 4ll));
EXPECT_EQ("1.5 foo 1e15 bar -3", str(1.5f, " foo ", 1e15, " bar ", -3));
EXPECT_EQ("foo", str('f', 'o', 'o'));
}
} // namespace
......
......@@ -21,7 +21,6 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define KJ_PRIVATE
#include "util.h"
#include "logging.h"
#include <stdio.h>
......
......@@ -124,11 +124,10 @@ inline size_t sum(std::initializer_list<size_t> nums) {
return result;
}
template <typename Element>
Element* fill(Element* ptr) { return ptr; }
inline char* fill(char* ptr) { return ptr; }
template <typename Element, typename First, typename... Rest>
Element* fill(Element* __restrict__ target, const First& first, Rest&&... rest) {
template <typename First, typename... Rest>
char* fill(char* __restrict__ target, const First& first, Rest&&... rest) {
auto i = first.begin();
auto end = first.end();
while (i != end) {
......@@ -137,23 +136,17 @@ Element* fill(Element* __restrict__ target, const First& first, Rest&&... rest)
return fill(target, std::forward<Rest>(rest)...);
}
template <typename Element, typename... Params>
Array<Element> concat(Params&&... params) {
template <typename... Params>
String concat(Params&&... params) {
// Concatenate a bunch of containers into a single Array. The containers can be anything that
// is iterable and whose elements can be converted to `Element`.
#ifdef __CDT_PARSER__
// Eclipse reports a bogus error on `size()`.
Array<Element> result;
#else
Array<Element> result = heapArray<Element>(sum({params.size()...}));
#endif
String result = heapString(sum({params.size()...}));
fill(result.begin(), std::forward<Params>(params)...);
return result;
}
template <typename Element>
Array<Element> concat(Array<Element>&& arr) {
inline String concat(String&& arr) {
return std::move(arr);
}
......@@ -177,6 +170,7 @@ struct Stringifier {
inline ArrayPtr<const char> operator*(const CappedArray<char, n>& s) const { return s; }
inline ArrayPtr<const char> operator*(const char* s) const { return arrayPtr(s, strlen(s)); }
inline ArrayPtr<const char> operator*(const String& s) const { return s.asArray(); }
inline ArrayPtr<const char> operator*(const StringPtr& s) const { return s.asArray(); }
inline FixedArray<char, 1> operator*(char c) const {
FixedArray<char, 1> result;
......@@ -209,18 +203,18 @@ CappedArray<char, sizeof(unsigned long) * 4> hex(unsigned long i);
CappedArray<char, sizeof(unsigned long long) * 4> hex(unsigned long long i);
template <typename... Params>
Array<char> str(Params&&... params) {
String str(Params&&... params) {
// Magic function which builds a string from a bunch of arbitrary values. Example:
// str(1, " / ", 2, " = ", 0.5)
// returns:
// "1 / 2 = 0.5"
// To teach `str` how to stringify a type, see `Stringifier`.
return concat<char>(STR * std::forward<Params>(params)...);
return concat(STR * std::forward<Params>(params)...);
}
template <typename T>
Array<char> strArray(T&& arr, const char* delim) {
String strArray(T&& arr, const char* delim) {
size_t delimLen = strlen(delim);
KJ_STACK_ARRAY(decltype(STR * arr[0]), pieces, arr.size(), 8, 32);
size_t size = 0;
......@@ -230,7 +224,7 @@ Array<char> strArray(T&& arr, const char* delim) {
size += pieces[i].size();
}
Array<char> result = heapArray<char>(size);
String result = heapString(size);
char* pos = result.begin();
for (size_t i = 0; i < arr.size(); i++) {
if (i > 0) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment