You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucy.apache.org by nw...@apache.org on 2013/09/01 22:17:07 UTC
[lucy-commits] [19/24] git commit: refs/heads/cfish-string-prep1 - Initial
implementation of CharBuf
Initial implementation of CharBuf
Mostly copied from String.
Project: http://git-wip-us.apache.org/repos/asf/lucy/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy/commit/0806c59c
Tree: http://git-wip-us.apache.org/repos/asf/lucy/tree/0806c59c
Diff: http://git-wip-us.apache.org/repos/asf/lucy/diff/0806c59c
Branch: refs/heads/cfish-string-prep1
Commit: 0806c59c453f49b7d4232cf48f87e5fe77453ba3
Parents: 71b1166
Author: Nick Wellnhofer <we...@aevum.de>
Authored: Sun Sep 1 18:01:20 2013 +0200
Committer: Nick Wellnhofer <we...@aevum.de>
Committed: Sun Sep 1 22:05:02 2013 +0200
----------------------------------------------------------------------
clownfish/runtime/core/Clownfish/CharBuf.c | 414 +++++++++++++++++++
clownfish/runtime/core/Clownfish/CharBuf.cfh | 152 +++++++
clownfish/runtime/core/Clownfish/Test.c | 2 +
.../runtime/core/Clownfish/Test/TestCharBuf.c | 300 ++++++++++++++
.../runtime/core/Clownfish/Test/TestCharBuf.cfh | 29 ++
5 files changed, 897 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/lucy/blob/0806c59c/clownfish/runtime/core/Clownfish/CharBuf.c
----------------------------------------------------------------------
diff --git a/clownfish/runtime/core/Clownfish/CharBuf.c b/clownfish/runtime/core/Clownfish/CharBuf.c
new file mode 100644
index 0000000..8b6de34
--- /dev/null
+++ b/clownfish/runtime/core/Clownfish/CharBuf.c
@@ -0,0 +1,414 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define C_CFISH_CHARBUF
+#define C_CFISH_STRING
+#define CFISH_USE_SHORT_NAMES
+#define CHY_USE_SHORT_NAMES
+
+#include "charmony.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "Clownfish/CharBuf.h"
+
+#include "Clownfish/Err.h"
+#include "Clownfish/String.h"
+#include "Clownfish/Util/Memory.h"
+#include "Clownfish/Util/StringHelper.h"
+#include "Clownfish/VTable.h"
+
+// Helper function for throwing invalid UTF-8 error. Since THROW uses
+// a String internally, calling THROW with invalid UTF-8 would create an
+// infinite loop -- so we fwrite some of the bogus text to stderr and
+// invoke THROW with a generic message.
+#define DIE_INVALID_UTF8(text, size) \
+ S_die_invalid_utf8(text, size, __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO)
+static void
+S_die_invalid_utf8(const char *text, size_t size, const char *file, int line,
+ const char *func);
+
+// Helper function for throwing invalid pattern error.
+static void
+S_die_invalid_pattern(const char *pattern);
+
+CharBuf*
+CB_new(size_t size) {
+ CharBuf *self = (CharBuf*)VTable_Make_Obj(CHARBUF);
+ return CB_init(self, size);
+}
+
+CharBuf*
+CB_init(CharBuf *self, size_t size) {
+ // Derive.
+ self->ptr = (char*)MALLOCATE(size + 1);
+
+ // Init.
+ *self->ptr = '\0'; // Empty string.
+
+ // Assign.
+ self->size = 0;
+ self->cap = size + 1;
+
+ return self;
+}
+
+CharBuf*
+CB_new_from_str(String *string) {
+ return CB_new_from_trusted_utf8(string->ptr, string->size);
+}
+
+CharBuf*
+CB_new_from_utf8(const char *ptr, size_t size) {
+ if (!StrHelp_utf8_valid(ptr, size)) {
+ DIE_INVALID_UTF8(ptr, size);
+ }
+ return CB_new_from_trusted_utf8(ptr, size);
+}
+
+CharBuf*
+CB_new_from_trusted_utf8(const char *ptr, size_t size) {
+ CharBuf *self = (CharBuf*)VTable_Make_Obj(CHARBUF);
+
+ // Derive.
+ self->ptr = (char*)MALLOCATE(size + 1);
+
+ // Copy.
+ memcpy(self->ptr, ptr, size);
+
+ // Assign.
+ self->size = size;
+ self->cap = size + 1;
+ self->ptr[size] = '\0'; // Null terminate.
+
+ return self;
+}
+
+CharBuf*
+CB_newf(const char *pattern, ...) {
+ CharBuf *self = CB_new(strlen(pattern));
+ va_list args;
+ va_start(args, pattern);
+ CB_VCatF(self, pattern, args);
+ va_end(args);
+ return self;
+}
+
+void
+CB_Destroy_IMP(CharBuf *self) {
+ FREEMEM(self->ptr);
+ SUPER_DESTROY(self, CHARBUF);
+}
+
+static void
+S_grow(CharBuf *self, size_t size) {
+ if (size >= self->cap) {
+ CB_Grow(self, size);
+ }
+}
+
+char*
+CB_Grow_IMP(CharBuf *self, size_t size) {
+ if (size >= self->cap) {
+ self->cap = size + 1;
+ self->ptr = (char*)REALLOCATE(self->ptr, self->cap);
+ }
+ return self->ptr;
+}
+
+static void
+S_die_invalid_utf8(const char *text, size_t size, const char *file, int line,
+ const char *func) {
+ fprintf(stderr, "Invalid UTF-8, aborting: '");
+ fwrite(text, sizeof(char), size < 200 ? size : 200, stderr);
+ if (size > 200) { fwrite("[...]", sizeof(char), 5, stderr); }
+ fprintf(stderr, "' (length %lu)\n", (unsigned long)size);
+ Err_throw_at(ERR, file, line, func, "Invalid UTF-8");
+}
+
+static void
+S_die_invalid_pattern(const char *pattern) {
+ size_t pattern_len = strlen(pattern);
+ fprintf(stderr, "Invalid pattern, aborting: '");
+ fwrite(pattern, sizeof(char), pattern_len, stderr);
+ fprintf(stderr, "'\n");
+ THROW(ERR, "Invalid pattern.");
+}
+
+void
+CB_catf(CharBuf *self, const char *pattern, ...) {
+ va_list args;
+ va_start(args, pattern);
+ CB_VCatF(self, pattern, args);
+ va_end(args);
+}
+
+void
+CB_VCatF_IMP(CharBuf *self, const char *pattern, va_list args) {
+ size_t pattern_len = strlen(pattern);
+ const char *pattern_start = pattern;
+ const char *pattern_end = pattern + pattern_len;
+ char buf[64];
+
+ for (; pattern < pattern_end; pattern++) {
+ const char *slice_end = pattern;
+
+ // Consume all characters leading up to a '%'.
+ while (slice_end < pattern_end && *slice_end != '%') { slice_end++; }
+ if (pattern != slice_end) {
+ size_t size = slice_end - pattern;
+ CB_Cat_Trusted_UTF8(self, pattern, size);
+ pattern = slice_end;
+ }
+
+ if (pattern < pattern_end) {
+ pattern++; // Move past '%'.
+
+ switch (*pattern) {
+ case '%': {
+ CB_Cat_Trusted_UTF8(self, "%", 1);
+ }
+ break;
+ case 'o': {
+ Obj *obj = va_arg(args, Obj*);
+ if (!obj) {
+ CB_Cat_Trusted_UTF8(self, "[NULL]", 6);
+ }
+ else if (Obj_Is_A(obj, STRING)) {
+ CB_Cat(self, (String*)obj);
+ }
+ else {
+ String *string = Obj_To_String(obj);
+ CB_Cat(self, string);
+ DECREF(string);
+ }
+ }
+ break;
+ case 'i': {
+ int64_t val = 0;
+ size_t size;
+ if (pattern[1] == '8') {
+ val = va_arg(args, int32_t);
+ pattern++;
+ }
+ else if (pattern[1] == '3' && pattern[2] == '2') {
+ val = va_arg(args, int32_t);
+ pattern += 2;
+ }
+ else if (pattern[1] == '6' && pattern[2] == '4') {
+ val = va_arg(args, int64_t);
+ pattern += 2;
+ }
+ else {
+ S_die_invalid_pattern(pattern_start);
+ }
+ size = sprintf(buf, "%" PRId64, val);
+ CB_Cat_Trusted_UTF8(self, buf, size);
+ }
+ break;
+ case 'u': {
+ uint64_t val = 0;
+ size_t size;
+ if (pattern[1] == '8') {
+ val = va_arg(args, uint32_t);
+ pattern += 1;
+ }
+ else if (pattern[1] == '3' && pattern[2] == '2') {
+ val = va_arg(args, uint32_t);
+ pattern += 2;
+ }
+ else if (pattern[1] == '6' && pattern[2] == '4') {
+ val = va_arg(args, uint64_t);
+ pattern += 2;
+ }
+ else {
+ S_die_invalid_pattern(pattern_start);
+ }
+ size = sprintf(buf, "%" PRIu64, val);
+ CB_Cat_Trusted_UTF8(self, buf, size);
+ }
+ break;
+ case 'f': {
+ if (pattern[1] == '6' && pattern[2] == '4') {
+ double num = va_arg(args, double);
+ char bigbuf[512];
+ size_t size = sprintf(bigbuf, "%g", num);
+ CB_Cat_Trusted_UTF8(self, bigbuf, size);
+ pattern += 2;
+ }
+ else {
+ S_die_invalid_pattern(pattern_start);
+ }
+ }
+ break;
+ case 'x': {
+ if (pattern[1] == '3' && pattern[2] == '2') {
+ unsigned long val = va_arg(args, uint32_t);
+ size_t size = sprintf(buf, "%.8lx", val);
+ CB_Cat_Trusted_UTF8(self, buf, size);
+ pattern += 2;
+ }
+ else {
+ S_die_invalid_pattern(pattern_start);
+ }
+ }
+ break;
+ case 's': {
+ char *string = va_arg(args, char*);
+ if (string == NULL) {
+ CB_Cat_Trusted_UTF8(self, "[NULL]", 6);
+ }
+ else {
+ size_t size = strlen(string);
+ if (StrHelp_utf8_valid(string, size)) {
+ CB_Cat_Trusted_UTF8(self, string, size);
+ }
+ else {
+ CB_Cat_Trusted_UTF8(self, "[INVALID UTF8]", 14);
+ }
+ }
+ }
+ break;
+ default: {
+ // Assume NULL-terminated pattern string, which
+ // eliminates the need for bounds checking if '%' is
+ // the last visible character.
+ S_die_invalid_pattern(pattern_start);
+ }
+ }
+ }
+ }
+}
+
+String*
+CB_To_String_IMP(CharBuf *self) {
+ return Str_new_from_trusted_utf8(self->ptr, self->size);
+}
+
+String*
+CB_Yield_String_IMP(CharBuf *self) {
+ String *retval
+ = Str_new_steal_from_trusted_str(self->ptr, self->size, self->cap);
+ self->ptr = NULL;
+ self->size = 0;
+ self->cap = 0;
+ return retval;
+}
+
+void
+CB_Cat_Char_IMP(CharBuf *self, uint32_t code_point) {
+ const size_t MAX_UTF8_BYTES = 4;
+ if (self->size + MAX_UTF8_BYTES >= self->cap) {
+ S_grow(self, Memory_oversize(self->size + MAX_UTF8_BYTES,
+ sizeof(char)));
+ }
+ char *end = self->ptr + self->size;
+ size_t count = StrHelp_encode_utf8_char(code_point, (uint8_t*)end);
+ self->size += count;
+ *(end + count) = '\0';
+}
+
+CharBuf*
+CB_Clone_IMP(CharBuf *self) {
+ return CB_new_from_trusted_utf8(self->ptr, self->size);
+}
+
+void
+CB_Mimic_UTF8_IMP(CharBuf *self, const char* ptr, size_t size) {
+ if (!StrHelp_utf8_valid(ptr, size)) {
+ DIE_INVALID_UTF8(ptr, size);
+ }
+ if (size >= self->cap) { S_grow(self, size); }
+ memmove(self->ptr, ptr, size);
+ self->size = size;
+ self->ptr[size] = '\0';
+}
+
+void
+CB_Mimic_IMP(CharBuf *self, Obj *other) {
+ if (Obj_Is_A(other, CHARBUF)) {
+ CharBuf *twin = (CharBuf*)other;
+ if (twin->size >= self->cap) { S_grow(self, twin->size); }
+ memmove(self->ptr, twin->ptr, twin->size);
+ self->size = twin->size;
+ self->ptr[twin->size] = '\0';
+ }
+ else if (Obj_Is_A(other, STRING)) {
+ String *twin = (String*)other;
+ if (twin->size >= self->cap) { S_grow(self, twin->size); }
+ memmove(self->ptr, twin->ptr, twin->size);
+ self->size = twin->size;
+ self->ptr[twin->size] = '\0';
+ }
+ else {
+ THROW(ERR, "CharBuf can't mimic %o", Obj_Get_Class_Name(other));
+ }
+}
+
+void
+CB_Cat_UTF8_IMP(CharBuf *self, const char* ptr, size_t size) {
+ if (!StrHelp_utf8_valid(ptr, size)) {
+ DIE_INVALID_UTF8(ptr, size);
+ }
+ CB_Cat_Trusted_UTF8_IMP(self, ptr, size);
+}
+
+void
+CB_Cat_Trusted_UTF8_IMP(CharBuf *self, const char* ptr, size_t size) {
+ const size_t new_size = self->size + size;
+ if (new_size >= self->cap) {
+ size_t amount = Memory_oversize(new_size, sizeof(char));
+ S_grow(self, amount);
+ }
+ memcpy((self->ptr + self->size), ptr, size);
+ self->size = new_size;
+ self->ptr[new_size] = '\0';
+}
+
+void
+CB_Cat_IMP(CharBuf *self, const String *string) {
+ const size_t new_size = self->size + string->size;
+ if (new_size >= self->cap) {
+ size_t amount = Memory_oversize(new_size, sizeof(char));
+ S_grow(self, amount);
+ }
+ memcpy((self->ptr + self->size), string->ptr, string->size);
+ self->size = new_size;
+ self->ptr[new_size] = '\0';
+}
+
+void
+CB_Set_Size_IMP(CharBuf *self, size_t size) {
+ if (size >= self->cap) {
+ THROW(ERR, "Can't set size of CharBuf beyond capacity");
+ }
+ self->size = size;
+}
+
+size_t
+CB_Get_Size_IMP(CharBuf *self) {
+ return self->size;
+}
+
+uint8_t*
+CB_Get_Ptr8_IMP(CharBuf *self) {
+ return (uint8_t*)self->ptr;
+}
+
+
http://git-wip-us.apache.org/repos/asf/lucy/blob/0806c59c/clownfish/runtime/core/Clownfish/CharBuf.cfh
----------------------------------------------------------------------
diff --git a/clownfish/runtime/core/Clownfish/CharBuf.cfh b/clownfish/runtime/core/Clownfish/CharBuf.cfh
new file mode 100644
index 0000000..105386c
--- /dev/null
+++ b/clownfish/runtime/core/Clownfish/CharBuf.cfh
@@ -0,0 +1,152 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+parcel Clownfish;
+
+/**
+ * Growable buffer holding Unicode characters.
+ */
+
+class Clownfish::CharBuf cnick CB
+ inherits Clownfish::Obj {
+
+ char *ptr;
+ size_t size;
+ size_t cap; /* allocated bytes, including terminating null */
+
+ inert incremented CharBuf*
+ new(size_t size);
+
+ inert CharBuf*
+ init(CharBuf *self, size_t size);
+
+ /** Return a new CharBuf which holds a copy of the passed-in String.
+ */
+ inert incremented CharBuf*
+ new_from_str(String *string);
+
+ /** Return a new CharBuf which holds a copy of the passed-in string.
+ * Check for UTF-8 validity.
+ */
+ inert incremented CharBuf*
+ new_from_utf8(const char *utf8, size_t size);
+
+ /** Return a new CharBuf which holds a copy of the passed-in string. No
+ * validity checking is performed.
+ */
+ inert incremented CharBuf*
+ new_from_trusted_utf8(const char *utf8, size_t size);
+
+ /** Return a pointer to a new CharBuf which contains formatted data
+ * expanded according to Str_VCatF.
+ *
+ * Note: a user-supplied <code>pattern</code> string is a security hole
+ * and must not be allowed.
+ */
+ inert incremented CharBuf*
+ newf(const char *pattern, ...);
+
+ public void
+ Mimic(CharBuf *self, Obj *other);
+
+ void
+ Mimic_UTF8(CharBuf *self, const char *ptr, size_t size);
+
+ /** Concatenate the passed-in string onto the end of the CharBuf.
+ */
+ void
+ Cat_UTF8(CharBuf *self, const char *ptr, size_t size);
+
+ /** Concatenate the supplied text onto the end of the CharBuf. Don't
+ * check for UTF-8 validity.
+ */
+ void
+ Cat_Trusted_UTF8(CharBuf *self, const char *ptr, size_t size);
+
+ /** Concatenate the contents of <code>string</code> onto the end of the
+ * caller.
+ */
+ void
+ Cat(CharBuf *self, const String *string);
+
+ /** Concatenate formatted arguments. Similar to the printf family, but
+ * only accepts minimal options (just enough for decent error messages).
+ *
+ * Objects: %o
+ * char*: %s
+ * integers: %i8 %i32 %i64 %u8 %u32 %u64
+ * floats: %f64
+ * hex: %x32
+ *
+ * Note that all Clownfish Objects, including CharBufs, are printed via
+ * %o (which invokes Obj_To_CharBuf()).
+ */
+ void
+ VCatF(CharBuf *self, const char *pattern, va_list args);
+
+ /** Invokes Str_VCatF to concatenate formatted arguments. Note that this
+ * is only a function and not a method.
+ */
+ inert void
+ catf(CharBuf *self, const char *pattern, ...);
+
+ /** Concatenate one Unicode character onto the end of the CharBuf.
+ */
+ void
+ Cat_Char(CharBuf *self, uint32_t code_point);
+
+ /** Assign more memory to the CharBuf, if it doesn't already have enough
+ * room to hold a string of <code>size</code> bytes. Cannot shrink the
+ * allocation.
+ *
+ * @return a pointer to the raw buffer.
+ */
+ char*
+ Grow(CharBuf *self, size_t size);
+
+ /** Set the String's <code>size</code> attribute.
+ */
+ void
+ Set_Size(CharBuf *self, size_t size);
+
+ /** Get the CharBuf's <code>size</code> attribute.
+ */
+ size_t
+ Get_Size(CharBuf *self);
+
+ /** Return the internal backing array for the CharBuf if its internal
+ * encoding is UTF-8. If it is not encoded as UTF-8 throw an exception.
+ */
+ uint8_t*
+ Get_Ptr8(CharBuf *self);
+
+ public incremented CharBuf*
+ Clone(CharBuf *self);
+
+ public void
+ Destroy(CharBuf *self);
+
+ public incremented String*
+ To_String(CharBuf *self);
+
+ /** Return the content of the CharBuf as String and clear the CharBuf.
+ * This is more efficient than To_String().
+ */
+ public incremented String*
+ Yield_String(CharBuf *self);
+}
+
+
http://git-wip-us.apache.org/repos/asf/lucy/blob/0806c59c/clownfish/runtime/core/Clownfish/Test.c
----------------------------------------------------------------------
diff --git a/clownfish/runtime/core/Clownfish/Test.c b/clownfish/runtime/core/Clownfish/Test.c
index 23137e1..b891331 100644
--- a/clownfish/runtime/core/Clownfish/Test.c
+++ b/clownfish/runtime/core/Clownfish/Test.c
@@ -24,6 +24,7 @@
#include "Clownfish/Test/TestByteBuf.h"
#include "Clownfish/Test/TestString.h"
+#include "Clownfish/Test/TestCharBuf.h"
#include "Clownfish/Test/TestErr.h"
#include "Clownfish/Test/TestHash.h"
#include "Clownfish/Test/TestLockFreeRegistry.h"
@@ -45,6 +46,7 @@ Test_create_test_suite() {
TestSuite_Add_Batch(suite, (TestBatch*)TestErr_new());
TestSuite_Add_Batch(suite, (TestBatch*)TestBB_new());
TestSuite_Add_Batch(suite, (TestBatch*)TestStr_new());
+ TestSuite_Add_Batch(suite, (TestBatch*)TestCB_new());
TestSuite_Add_Batch(suite, (TestBatch*)TestNumUtil_new());
TestSuite_Add_Batch(suite, (TestBatch*)TestNum_new());
TestSuite_Add_Batch(suite, (TestBatch*)TestStrHelp_new());
http://git-wip-us.apache.org/repos/asf/lucy/blob/0806c59c/clownfish/runtime/core/Clownfish/Test/TestCharBuf.c
----------------------------------------------------------------------
diff --git a/clownfish/runtime/core/Clownfish/Test/TestCharBuf.c b/clownfish/runtime/core/Clownfish/Test/TestCharBuf.c
new file mode 100644
index 0000000..31865f9
--- /dev/null
+++ b/clownfish/runtime/core/Clownfish/Test/TestCharBuf.c
@@ -0,0 +1,300 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+#include <stdio.h>
+
+#define CHY_USE_SHORT_NAMES
+#define CFISH_USE_SHORT_NAMES
+#define TESTCFISH_USE_SHORT_NAMES
+
+#include "charmony.h"
+
+#include "Clownfish/Test/TestCharBuf.h"
+
+#include "Clownfish/CharBuf.h"
+#include "Clownfish/Num.h"
+#include "Clownfish/String.h"
+#include "Clownfish/Test.h"
+#include "Clownfish/TestHarness/TestBatchRunner.h"
+#include "Clownfish/TestHarness/TestUtils.h"
+#include "Clownfish/VTable.h"
+
+static char smiley[] = { (char)0xE2, (char)0x98, (char)0xBA, 0 };
+static uint32_t smiley_len = 3;
+
+TestCharBuf*
+TestCB_new() {
+ return (TestCharBuf*)VTable_Make_Obj(TESTCHARBUF);
+}
+
+static CharBuf*
+S_get_cb(const char *string) {
+ return CB_new_from_utf8(string, strlen(string));
+}
+
+static String*
+S_get_str(const char *string) {
+ return Str_new_from_utf8(string, strlen(string));
+}
+
+static bool
+S_cb_equals(CharBuf *cb, String *other) {
+ String *string = CB_To_String(cb);
+ bool retval = Str_Equals(string, (Obj*)other);
+ DECREF(string);
+ return retval;
+}
+
+static void
+test_Cat(TestBatchRunner *runner) {
+ String *wanted = Str_newf("a%s", smiley);
+ CharBuf *got = S_get_cb("");
+
+ CB_Cat(got, wanted);
+ TEST_TRUE(runner, S_cb_equals(got, wanted), "Cat");
+ DECREF(got);
+
+ got = S_get_cb("a");
+ CB_Cat_Char(got, 0x263A);
+ TEST_TRUE(runner, S_cb_equals(got, wanted), "Cat_Char");
+ DECREF(got);
+
+ got = S_get_cb("a");
+ CB_Cat_UTF8(got, smiley, smiley_len);
+ TEST_TRUE(runner, S_cb_equals(got, wanted), "Cat_UTF8");
+ DECREF(got);
+
+ got = S_get_cb("a");
+ CB_Cat_Trusted_UTF8(got, smiley, smiley_len);
+ TEST_TRUE(runner, S_cb_equals(got, wanted), "Cat_Trusted_UTF8");
+ DECREF(got);
+
+ DECREF(wanted);
+}
+
+static void
+test_Mimic_and_Clone(TestBatchRunner *runner) {
+ String *wanted = S_get_str("foo");
+ CharBuf *wanted_cb = S_get_cb("foo");
+ CharBuf *got = S_get_cb("bar");
+
+ CB_Mimic(got, (Obj*)wanted);
+ TEST_TRUE(runner, S_cb_equals(got, wanted), "Mimic String");
+ DECREF(got);
+
+ got = S_get_cb("bar");
+ CB_Mimic(got, (Obj*)wanted_cb);
+ TEST_TRUE(runner, S_cb_equals(got, wanted), "Mimic CharBuf");
+ DECREF(got);
+
+ got = S_get_cb("bar");
+ CB_Mimic_UTF8(got, "foo", 3);
+ TEST_TRUE(runner, S_cb_equals(got, wanted), "Mimic_Str");
+ DECREF(got);
+
+ got = CB_Clone(wanted_cb);
+ TEST_TRUE(runner, S_cb_equals(got, wanted), "Clone");
+ DECREF(got);
+
+ DECREF(wanted);
+}
+
+/*
+static void
+test_Truncate(TestBatchRunner *runner) {
+ String *wanted = Str_newf("a%s", smiley);
+ CharBuf *got = CB_newf("a%s%sb%sc", smiley, smiley, smiley);
+ CB_Truncate(got, 2);
+ TEST_TRUE(runner, S_cb_equals(got, wanted), "Truncate");
+ DECREF(wanted);
+ DECREF(got);
+}
+*/
+
+static void
+test_vcatf_s(TestBatchRunner *runner) {
+ String *wanted = S_get_str("foo bar bizzle baz");
+ CharBuf *got = S_get_cb("foo ");
+ CB_catf(got, "bar %s baz", "bizzle");
+ TEST_TRUE(runner, S_cb_equals(got, wanted), "%%s");
+ DECREF(wanted);
+ DECREF(got);
+}
+
+static void
+test_vcatf_null_string(TestBatchRunner *runner) {
+ String *wanted = S_get_str("foo bar [NULL] baz");
+ CharBuf *got = S_get_cb("foo ");
+ CB_catf(got, "bar %s baz", NULL);
+ TEST_TRUE(runner, S_cb_equals(got, wanted), "%%s NULL");
+ DECREF(wanted);
+ DECREF(got);
+}
+
+static void
+test_vcatf_str(TestBatchRunner *runner) {
+ String *wanted = S_get_str("foo bar ZEKE baz");
+ String *catworthy = S_get_str("ZEKE");
+ CharBuf *got = S_get_cb("foo ");
+ CB_catf(got, "bar %o baz", catworthy);
+ TEST_TRUE(runner, S_cb_equals(got, wanted), "%%o CharBuf");
+ DECREF(catworthy);
+ DECREF(wanted);
+ DECREF(got);
+}
+
+static void
+test_vcatf_obj(TestBatchRunner *runner) {
+ String *wanted = S_get_str("ooga 20 booga");
+ Integer32 *i32 = Int32_new(20);
+ CharBuf *got = S_get_cb("ooga");
+ CB_catf(got, " %o booga", i32);
+ TEST_TRUE(runner, S_cb_equals(got, wanted), "%%o Obj");
+ DECREF(i32);
+ DECREF(wanted);
+ DECREF(got);
+}
+
+static void
+test_vcatf_null_obj(TestBatchRunner *runner) {
+ String *wanted = S_get_str("foo bar [NULL] baz");
+ CharBuf *got = S_get_cb("foo ");
+ CB_catf(got, "bar %o baz", NULL);
+ TEST_TRUE(runner, S_cb_equals(got, wanted), "%%o NULL");
+ DECREF(wanted);
+ DECREF(got);
+}
+
+static void
+test_vcatf_i8(TestBatchRunner *runner) {
+ String *wanted = S_get_str("foo bar -3 baz");
+ int8_t num = -3;
+ CharBuf *got = S_get_cb("foo ");
+ CB_catf(got, "bar %i8 baz", num);
+ TEST_TRUE(runner, S_cb_equals(got, wanted), "%%i8");
+ DECREF(wanted);
+ DECREF(got);
+}
+
+static void
+test_vcatf_i32(TestBatchRunner *runner) {
+ String *wanted = S_get_str("foo bar -100000 baz");
+ int32_t num = -100000;
+ CharBuf *got = S_get_cb("foo ");
+ CB_catf(got, "bar %i32 baz", num);
+ TEST_TRUE(runner, S_cb_equals(got, wanted), "%%i32");
+ DECREF(wanted);
+ DECREF(got);
+}
+
+static void
+test_vcatf_i64(TestBatchRunner *runner) {
+ String *wanted = S_get_str("foo bar -5000000000 baz");
+ int64_t num = INT64_C(-5000000000);
+ CharBuf *got = S_get_cb("foo ");
+ CB_catf(got, "bar %i64 baz", num);
+ TEST_TRUE(runner, S_cb_equals(got, wanted), "%%i64");
+ DECREF(wanted);
+ DECREF(got);
+}
+
+static void
+test_vcatf_u8(TestBatchRunner *runner) {
+ String *wanted = S_get_str("foo bar 3 baz");
+ uint8_t num = 3;
+ CharBuf *got = S_get_cb("foo ");
+ CB_catf(got, "bar %u8 baz", num);
+ TEST_TRUE(runner, S_cb_equals(got, wanted), "%%u8");
+ DECREF(wanted);
+ DECREF(got);
+}
+
+static void
+test_vcatf_u32(TestBatchRunner *runner) {
+ String *wanted = S_get_str("foo bar 100000 baz");
+ uint32_t num = 100000;
+ CharBuf *got = S_get_cb("foo ");
+ CB_catf(got, "bar %u32 baz", num);
+ TEST_TRUE(runner, S_cb_equals(got, wanted), "%%u32");
+ DECREF(wanted);
+ DECREF(got);
+}
+
+static void
+test_vcatf_u64(TestBatchRunner *runner) {
+ String *wanted = S_get_str("foo bar 5000000000 baz");
+ uint64_t num = UINT64_C(5000000000);
+ CharBuf *got = S_get_cb("foo ");
+ CB_catf(got, "bar %u64 baz", num);
+ TEST_TRUE(runner, S_cb_equals(got, wanted), "%%u64");
+ DECREF(wanted);
+ DECREF(got);
+}
+
+static void
+test_vcatf_f64(TestBatchRunner *runner) {
+ String *wanted;
+ char buf[64];
+ float num = 1.3f;
+ CharBuf *got = S_get_cb("foo ");
+ sprintf(buf, "foo bar %g baz", num);
+ wanted = Str_new_from_trusted_utf8(buf, strlen(buf));
+ CB_catf(got, "bar %f64 baz", num);
+ TEST_TRUE(runner, S_cb_equals(got, wanted), "%%f64");
+ DECREF(wanted);
+ DECREF(got);
+}
+
+static void
+test_vcatf_x32(TestBatchRunner *runner) {
+ String *wanted;
+ char buf[64];
+ unsigned long num = INT32_MAX;
+ CharBuf *got = S_get_cb("foo ");
+#if (SIZEOF_LONG == 4)
+ sprintf(buf, "foo bar %.8lx baz", num);
+#elif (SIZEOF_INT == 4)
+ sprintf(buf, "foo bar %.8x baz", (unsigned)num);
+#endif
+ wanted = Str_new_from_trusted_utf8(buf, strlen(buf));
+ CB_catf(got, "bar %x32 baz", (uint32_t)num);
+ TEST_TRUE(runner, S_cb_equals(got, wanted), "%%x32");
+ DECREF(wanted);
+ DECREF(got);
+}
+
+void
+TestCB_Run_IMP(TestCharBuf *self, TestBatchRunner *runner) {
+ TestBatchRunner_Plan(runner, (TestBatch*)self, 21);
+ test_vcatf_s(runner);
+ test_vcatf_null_string(runner);
+ test_vcatf_str(runner);
+ test_vcatf_obj(runner);
+ test_vcatf_null_obj(runner);
+ test_vcatf_i8(runner);
+ test_vcatf_i32(runner);
+ test_vcatf_i64(runner);
+ test_vcatf_u8(runner);
+ test_vcatf_u32(runner);
+ test_vcatf_u64(runner);
+ test_vcatf_f64(runner);
+ test_vcatf_x32(runner);
+ test_Cat(runner);
+ test_Mimic_and_Clone(runner);
+}
+
+
http://git-wip-us.apache.org/repos/asf/lucy/blob/0806c59c/clownfish/runtime/core/Clownfish/Test/TestCharBuf.cfh
----------------------------------------------------------------------
diff --git a/clownfish/runtime/core/Clownfish/Test/TestCharBuf.cfh b/clownfish/runtime/core/Clownfish/Test/TestCharBuf.cfh
new file mode 100644
index 0000000..3cc5d43
--- /dev/null
+++ b/clownfish/runtime/core/Clownfish/Test/TestCharBuf.cfh
@@ -0,0 +1,29 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+parcel TestClownfish;
+
+class Clownfish::Test::TestCharBuf cnick TestCB
+ inherits Clownfish::TestHarness::TestBatch {
+
+ inert incremented TestCharBuf*
+ new();
+
+ void
+ Run(TestCharBuf *self, TestBatchRunner *runner);
+}
+
+