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:01 UTC

[lucy-commits] [13/24] Rename CharBuf to String (cnick Str)

http://git-wip-us.apache.org/repos/asf/lucy/blob/2c3dbf15/clownfish/runtime/core/Clownfish/Err.c
----------------------------------------------------------------------
diff --git a/clownfish/runtime/core/Clownfish/Err.c b/clownfish/runtime/core/Clownfish/Err.c
index 68bc66f..46f0027 100644
--- a/clownfish/runtime/core/Clownfish/Err.c
+++ b/clownfish/runtime/core/Clownfish/Err.c
@@ -27,18 +27,18 @@
 #include <ctype.h>
 
 #include "Clownfish/Err.h"
-#include "Clownfish/CharBuf.h"
+#include "Clownfish/String.h"
 #include "Clownfish/VTable.h"
 #include "Clownfish/Util/Memory.h"
 
 Err*
-Err_new(CharBuf *mess) {
+Err_new(String *mess) {
     Err *self = (Err*)VTable_Make_Obj(ERR);
     return Err_init(self, mess);
 }
 
 Err*
-Err_init(Err *self, CharBuf *mess) {
+Err_init(Err *self, String *mess) {
     self->mess = mess;
     return self;
 }
@@ -49,14 +49,14 @@ Err_Destroy_IMP(Err *self) {
     SUPER_DESTROY(self, ERR);
 }
 
-CharBuf*
+String*
 Err_To_String_IMP(Err *self) {
-    return (CharBuf*)INCREF(self->mess);
+    return (String*)INCREF(self->mess);
 }
 
 void
-Err_Cat_Mess_IMP(Err *self, const CharBuf *mess) {
-    CB_Cat(self->mess, mess);
+Err_Cat_Mess_IMP(Err *self, const String *mess) {
+    Str_Cat(self->mess, mess);
 }
 
 // Fallbacks in case variadic macros aren't available.
@@ -65,10 +65,10 @@ void
 THROW(VTable *vtable, char *pattern, ...) {
     va_list args;
     Err *err = (Err*)VTable_Make_Obj(vtable);
-    err = Err_init(err, CB_new(0));
+    err = Err_init(err, Str_new(0));
 
     va_start(args, pattern);
-    CB_VCatF(err->mess, pattern, args);
+    Str_VCatF(err->mess, pattern, args);
     va_end(args);
 
     Err_do_throw(err);
@@ -76,21 +76,21 @@ THROW(VTable *vtable, char *pattern, ...) {
 void
 CFISH_WARN(char *pattern, ...) {
     va_list args;
-    CharBuf *const message = CB_new(strlen(pattern) + 10);
+    String *const message = Str_new(strlen(pattern) + 10);
 
     va_start(args, pattern);
-    CB_VCatF(message, pattern, args);
+    Str_VCatF(message, pattern, args);
     va_end(args);
 
     Err_warn_mess(message);
 }
-CharBuf*
+String*
 CFISH_MAKE_MESS(char *pattern, ...) {
     va_list args;
-    CharBuf *const message = CB_new(strlen(pattern) + 10);
+    String *const message = Str_new(strlen(pattern) + 10);
 
     va_start(args, pattern);
-    CB_VCatF(message, pattern, args);
+    Str_VCatF(message, pattern, args);
     va_end(args);
 
     return message;
@@ -99,28 +99,28 @@ CFISH_MAKE_MESS(char *pattern, ...) {
 
 
 static void
-S_vcat_mess(CharBuf *message, const char *file, int line, const char *func,
+S_vcat_mess(String *message, const char *file, int line, const char *func,
             const char *pattern, va_list args) {
     size_t guess_len = strlen(file)
                        + func ? strlen(func) : 0
                        + strlen(pattern)
                        + 30;
-    CB_Grow(message, guess_len);
-    CB_VCatF(message, pattern, args);
+    Str_Grow(message, guess_len);
+    Str_VCatF(message, pattern, args);
     if (func != NULL) {
-        CB_catf(message, "\n\t%s at %s line %i32\n", func, file, (int32_t)line);
+        Str_catf(message, "\n\t%s at %s line %i32\n", func, file, (int32_t)line);
     }
     else {
-        CB_catf(message, "\n\t%s line %i32\n", file, (int32_t)line);
+        Str_catf(message, "\n\t%s line %i32\n", file, (int32_t)line);
     }
 }
 
-CharBuf*
+String*
 Err_make_mess(const char *file, int line, const char *func,
               const char *pattern, ...) {
     va_list args;
     size_t guess_len = strlen(pattern) + strlen(file) + 20;
-    CharBuf *message = CB_new(guess_len);
+    String *message = Str_new(guess_len);
     va_start(args, pattern);
     S_vcat_mess(message, file, line, func, pattern, args);
     va_end(args);
@@ -131,30 +131,30 @@ void
 Err_warn_at(const char *file, int line, const char *func,
             const char *pattern, ...) {
     va_list args;
-    CharBuf *message = CB_new(0);
+    String *message = Str_new(0);
     va_start(args, pattern);
     S_vcat_mess(message, file, line, func, pattern, args);
     va_end(args);
     Err_warn_mess(message);
 }
 
-CharBuf*
+String*
 Err_Get_Mess_IMP(Err *self) {
     return self->mess;
 }
 
 void
 Err_Add_Frame_IMP(Err *self, const char *file, int line, const char *func) {
-    if (!CB_Ends_With_Str(self->mess, "\n", 1)) {
-        CB_Cat_Char(self->mess, '\n');
+    if (!Str_Ends_With_Str(self->mess, "\n", 1)) {
+        Str_Cat_Char(self->mess, '\n');
     }
 
     if (func != NULL) {
-        CB_catf(self->mess, "\t%s at %s line %i32\n", func, file,
+        Str_catf(self->mess, "\t%s at %s line %i32\n", func, file,
                 (int32_t)line);
     }
     else {
-        CB_catf(self->mess, "\tat %s line %i32\n", file, (int32_t)line);
+        Str_catf(self->mess, "\tat %s line %i32\n", file, (int32_t)line);
     }
 }
 
@@ -169,7 +169,7 @@ Err_throw_at(VTable *vtable, const char *file, int line,
              const char *func, const char *pattern, ...) {
     va_list args;
     Err *err = (Err*)VTable_Make_Obj(vtable);
-    err = Err_init(err, CB_new(0));
+    err = Err_init(err, Str_new(0));
 
     va_start(args, pattern);
     S_vcat_mess(err->mess, file, line, func, pattern, args);

http://git-wip-us.apache.org/repos/asf/lucy/blob/2c3dbf15/clownfish/runtime/core/Clownfish/Err.cfh
----------------------------------------------------------------------
diff --git a/clownfish/runtime/core/Clownfish/Err.cfh b/clownfish/runtime/core/Clownfish/Err.cfh
index aa25b3e..3f47e4f 100644
--- a/clownfish/runtime/core/Clownfish/Err.cfh
+++ b/clownfish/runtime/core/Clownfish/Err.cfh
@@ -38,21 +38,21 @@ __END_C__
  */
 public class Clownfish::Err inherits Clownfish::Obj {
 
-    CharBuf *mess;
+    String *mess;
 
     inert void
     init_class();
 
     inert incremented Err*
-    new(decremented CharBuf *mess);
+    new(decremented String *mess);
 
     inert Err*
-    init(Err *self, decremented CharBuf *mess);
+    init(Err *self, decremented String *mess);
 
     public void
     Destroy(Err *self);
 
-    public incremented CharBuf*
+    public incremented String*
     To_String(Err *self);
 
     void*
@@ -61,9 +61,9 @@ public class Clownfish::Err inherits Clownfish::Obj {
     /** Concatenate the supplied argument onto the internal "mess".
      */
     public void
-    Cat_Mess(Err *self, const CharBuf *mess);
+    Cat_Mess(Err *self, const String *mess);
 
-    public CharBuf*
+    public String*
     Get_Mess(Err *self);
 
     /** Add information about the current stack frame onto <code>mess</code>.
@@ -117,7 +117,7 @@ public class Clownfish::Err inherits Clownfish::Obj {
      * @param message Error message, to be output verbatim.
      */
     inert void
-    throw_mess(VTable *vtable, decremented CharBuf *message);
+    throw_mess(VTable *vtable, decremented String *message);
 
     /** Invoke host exception handling.
      */
@@ -130,12 +130,12 @@ public class Clownfish::Err inherits Clownfish::Obj {
      * @param message Error message, to be output verbatim.
      */
     inert void
-    warn_mess(decremented CharBuf *message);
+    warn_mess(decremented String *message);
 
     /** Create a formatted error message.  Ususally invoked via the MAKE_MESS
      * macro.
      */
-    inert CharBuf*
+    inert String*
     make_mess(const char *file, int line, const char *func,
               const char *pattern, ...);
 
@@ -211,7 +211,7 @@ __C__
   CFISH_THROW(cfish_VTable *vtable, char* format, ...);
   void
   CFISH_WARN(char* format, ...);
-  cfish_CharBuf*
+  cfish_String*
   CFISH_MAKE_MESS(char* format, ...);
 #endif
 
@@ -228,7 +228,7 @@ static CFISH_INLINE void
 cfish_Err_abstract_class_check(cfish_Obj *obj, cfish_VTable *vtable) {
     cfish_VTable *const my_vtable = (cfish_VTable*)((cfish_Dummy*)obj)->vtable;
     if (my_vtable == vtable) {
-        cfish_CharBuf *mess = CFISH_MAKE_MESS("%o is an abstract class",
+        cfish_String *mess = CFISH_MAKE_MESS("%o is an abstract class",
                                               CFISH_Obj_Get_Class_Name(obj));
         CFISH_Obj_Dec_RefCount(obj);
         cfish_Err_throw_mess(CFISH_ERR, mess);

http://git-wip-us.apache.org/repos/asf/lucy/blob/2c3dbf15/clownfish/runtime/core/Clownfish/Hash.c
----------------------------------------------------------------------
diff --git a/clownfish/runtime/core/Clownfish/Hash.c b/clownfish/runtime/core/Clownfish/Hash.c
index 1fbfe42..7e3fb2e 100644
--- a/clownfish/runtime/core/Clownfish/Hash.c
+++ b/clownfish/runtime/core/Clownfish/Hash.c
@@ -27,7 +27,7 @@
 #include "Clownfish/VTable.h"
 
 #include "Clownfish/Hash.h"
-#include "Clownfish/CharBuf.h"
+#include "Clownfish/String.h"
 #include "Clownfish/Err.h"
 #include "Clownfish/VArray.h"
 #include "Clownfish/Util/Memory.h"

http://git-wip-us.apache.org/repos/asf/lucy/blob/2c3dbf15/clownfish/runtime/core/Clownfish/Method.c
----------------------------------------------------------------------
diff --git a/clownfish/runtime/core/Clownfish/Method.c b/clownfish/runtime/core/Clownfish/Method.c
index 0ad01f8..9f4bddd 100644
--- a/clownfish/runtime/core/Clownfish/Method.c
+++ b/clownfish/runtime/core/Clownfish/Method.c
@@ -21,20 +21,20 @@
 #include "charmony.h"
 
 #include "Clownfish/Method.h"
-#include "Clownfish/CharBuf.h"
+#include "Clownfish/String.h"
 #include "Clownfish/Err.h"
 #include "Clownfish/VTable.h"
 
 Method*
-Method_new(const CharBuf *name, cfish_method_t callback_func, size_t offset) {
+Method_new(const String *name, cfish_method_t callback_func, size_t offset) {
     Method *self = (Method*)VTable_Make_Obj(METHOD);
     return Method_init(self, name, callback_func, offset);
 }
 
 Method*
-Method_init(Method *self, const CharBuf *name, cfish_method_t callback_func,
+Method_init(Method *self, const String *name, cfish_method_t callback_func,
             size_t offset) {
-    self->name          = CB_Clone(name);
+    self->name          = Str_Clone(name);
     self->host_alias    = NULL;
     self->callback_func = callback_func;
     self->offset        = offset;
@@ -65,12 +65,12 @@ Method_Get_RefCount_IMP(Method *self) {
     return 1;
 }
 
-CharBuf*
+String*
 Method_Get_Name_IMP(Method *self) {
     return self->name;
 }
 
-CharBuf*
+String*
 Method_Get_Host_Alias_IMP(Method *self) {
     return self->host_alias;
 }

http://git-wip-us.apache.org/repos/asf/lucy/blob/2c3dbf15/clownfish/runtime/core/Clownfish/Method.cfh
----------------------------------------------------------------------
diff --git a/clownfish/runtime/core/Clownfish/Method.cfh b/clownfish/runtime/core/Clownfish/Method.cfh
index 9bd32e2..521839c 100644
--- a/clownfish/runtime/core/Clownfish/Method.cfh
+++ b/clownfish/runtime/core/Clownfish/Method.cfh
@@ -21,23 +21,23 @@ parcel Clownfish;
 
 class Clownfish::Method inherits Clownfish::Obj {
 
-    CharBuf        *name;
-    CharBuf        *host_alias;
+    String         *name;
+    String         *host_alias;
     cfish_method_t  callback_func;
     size_t          offset;
     bool            is_excluded;
 
     inert Method*
-    new(const CharBuf *name, cfish_method_t callback_func, size_t offset);
+    new(const String *name, cfish_method_t callback_func, size_t offset);
 
     inert Method*
-    init(Method *self, const CharBuf *name, cfish_method_t callback_func,
+    init(Method *self, const String *name, cfish_method_t callback_func,
          size_t offset);
 
-    CharBuf*
+    String*
     Get_Name(Method *self);
 
-    nullable CharBuf*
+    nullable String*
     Get_Host_Alias(Method *self);
 
     bool

http://git-wip-us.apache.org/repos/asf/lucy/blob/2c3dbf15/clownfish/runtime/core/Clownfish/Num.c
----------------------------------------------------------------------
diff --git a/clownfish/runtime/core/Clownfish/Num.c b/clownfish/runtime/core/Clownfish/Num.c
index 71ca017..728c618 100644
--- a/clownfish/runtime/core/Clownfish/Num.c
+++ b/clownfish/runtime/core/Clownfish/Num.c
@@ -29,7 +29,7 @@
 #include "charmony.h"
 
 #include "Clownfish/Num.h"
-#include "Clownfish/CharBuf.h"
+#include "Clownfish/String.h"
 #include "Clownfish/Err.h"
 #include "Clownfish/VTable.h"
 
@@ -66,9 +66,9 @@ FloatNum_Compare_To_IMP(FloatNum *self, Obj *other) {
     return 0;
 }
 
-CharBuf*
+String*
 FloatNum_To_String_IMP(FloatNum *self) {
-    return CB_newf("%f64", FloatNum_To_F64(self));
+    return Str_newf("%f64", FloatNum_To_F64(self));
 }
 
 /***************************************************************************/
@@ -91,9 +91,9 @@ IntNum_Compare_To_IMP(IntNum *self, Obj *other) {
     return 0;
 }
 
-CharBuf*
+String*
 IntNum_To_String_IMP(IntNum *self) {
-    return CB_newf("%i64", IntNum_To_I64(self));
+    return Str_newf("%i64", IntNum_To_I64(self));
 }
 
 /***************************************************************************/
@@ -325,10 +325,10 @@ void
 Bool_init_class() {
     Bool_true_singleton          = (BoolNum*)VTable_Make_Obj(BOOLNUM);
     Bool_true_singleton->value   = true;
-    Bool_true_singleton->string  = CB_newf("true");
+    Bool_true_singleton->string  = Str_newf("true");
     Bool_false_singleton         = (BoolNum*)VTable_Make_Obj(BOOLNUM);
     Bool_false_singleton->value  = false;
-    Bool_false_singleton->string = CB_newf("false");
+    Bool_false_singleton->string = Str_newf("false");
 }
 
 BoolNum*
@@ -374,9 +374,9 @@ Bool_Hash_Sum_IMP(BoolNum *self) {
     return (int32_t)hash_sum;
 }
 
-CharBuf*
+String*
 Bool_To_String_IMP(BoolNum *self) {
-    return (CharBuf*)CB_Inc_RefCount(self->string);
+    return (String*)Str_Inc_RefCount(self->string);
 }
 
 bool

http://git-wip-us.apache.org/repos/asf/lucy/blob/2c3dbf15/clownfish/runtime/core/Clownfish/Num.cfh
----------------------------------------------------------------------
diff --git a/clownfish/runtime/core/Clownfish/Num.cfh b/clownfish/runtime/core/Clownfish/Num.cfh
index 3f6a8d6..56983ce 100644
--- a/clownfish/runtime/core/Clownfish/Num.cfh
+++ b/clownfish/runtime/core/Clownfish/Num.cfh
@@ -37,7 +37,7 @@ abstract class Clownfish::FloatNum inherits Clownfish::Num {
     public int32_t
     Compare_To(FloatNum *self, Obj *other);
 
-    public incremented CharBuf*
+    public incremented String*
     To_String(FloatNum *self);
 }
 
@@ -52,7 +52,7 @@ abstract class Clownfish::IntNum inherits Clownfish::Num {
     public int32_t
     Compare_To(IntNum *self, Obj *other);
 
-    public incremented CharBuf*
+    public incremented String*
     To_String(IntNum *self);
 }
 
@@ -219,7 +219,7 @@ class Clownfish::Integer64 cnick Int64
  */
 class Clownfish::BoolNum cnick Bool inherits Clownfish::IntNum {
     bool value;
-    CharBuf *string;
+    String *string;
 
     inert BoolNum *true_singleton;
     inert BoolNum *false_singleton;
@@ -258,7 +258,7 @@ class Clownfish::BoolNum cnick Bool inherits Clownfish::IntNum {
     public bool
     Equals(BoolNum *self, Obj *other);
 
-    public incremented CharBuf*
+    public incremented String*
     To_String(BoolNum *self);
 
     incremented BoolNum*

http://git-wip-us.apache.org/repos/asf/lucy/blob/2c3dbf15/clownfish/runtime/core/Clownfish/Obj.c
----------------------------------------------------------------------
diff --git a/clownfish/runtime/core/Clownfish/Obj.c b/clownfish/runtime/core/Clownfish/Obj.c
index 86d1019..0bc8b44 100644
--- a/clownfish/runtime/core/Clownfish/Obj.c
+++ b/clownfish/runtime/core/Clownfish/Obj.c
@@ -26,7 +26,7 @@
 #include <string.h>
 
 #include "Clownfish/Obj.h"
-#include "Clownfish/CharBuf.h"
+#include "Clownfish/String.h"
 #include "Clownfish/Err.h"
 #include "Clownfish/Hash.h"
 #include "Clownfish/VTable.h"
@@ -68,16 +68,16 @@ Obj_Equals_IMP(Obj *self, Obj *other) {
     return (self == other);
 }
 
-CharBuf*
+String*
 Obj_To_String_IMP(Obj *self) {
 #if (SIZEOF_PTR == 4)
-    return CB_newf("%o@0x%x32", Obj_Get_Class_Name(self), self);
+    return Str_newf("%o@0x%x32", Obj_Get_Class_Name(self), self);
 #elif (SIZEOF_PTR == 8)
     int64_t   iaddress   = PTR_TO_I64(self);
     uint64_t  address    = (uint64_t)iaddress;
     uint32_t  address_hi = address >> 32;
     uint32_t  address_lo = address & 0xFFFFFFFF;
-    return CB_newf("%o@0x%x32%x32", Obj_Get_Class_Name(self), address_hi,
+    return Str_newf("%o@0x%x32%x32", Obj_Get_Class_Name(self), address_hi,
                    address_lo);
 #else
   #error "Unexpected pointer size."
@@ -94,7 +94,7 @@ Obj_Get_VTable_IMP(Obj *self) {
     return self->vtable;
 }
 
-CharBuf*
+String*
 Obj_Get_Class_Name_IMP(Obj *self) {
     return VTable_Get_Name(self->vtable);
 }

http://git-wip-us.apache.org/repos/asf/lucy/blob/2c3dbf15/clownfish/runtime/core/Clownfish/Obj.cfh
----------------------------------------------------------------------
diff --git a/clownfish/runtime/core/Clownfish/Obj.cfh b/clownfish/runtime/core/Clownfish/Obj.cfh
index a755d5d..fafccfc 100644
--- a/clownfish/runtime/core/Clownfish/Obj.cfh
+++ b/clownfish/runtime/core/Clownfish/Obj.cfh
@@ -118,7 +118,7 @@ public class Clownfish::Obj {
 
     /** Return the name of the class that the object belongs to.
      */
-    public CharBuf*
+    public String*
     Get_Class_Name(Obj *self);
 
     /** Indicate whether the object is a descendent of <code>ancestor</code>.
@@ -128,7 +128,7 @@ public class Clownfish::Obj {
 
     /** Generic stringification: "ClassName@hex_mem_address".
      */
-    public incremented CharBuf*
+    public incremented String*
     To_String(Obj *self);
 
     /** Convert the object to a 64-bit integer.

http://git-wip-us.apache.org/repos/asf/lucy/blob/2c3dbf15/clownfish/runtime/core/Clownfish/String.c
----------------------------------------------------------------------
diff --git a/clownfish/runtime/core/Clownfish/String.c b/clownfish/runtime/core/Clownfish/String.c
new file mode 100644
index 0000000..3cf3668
--- /dev/null
+++ b/clownfish/runtime/core/Clownfish/String.c
@@ -0,0 +1,912 @@
+/* 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_STRING
+#define C_CFISH_VIEWCHARBUF
+#define C_CFISH_STACKSTRING
+#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/VTable.h"
+#include "Clownfish/String.h"
+
+#include "Clownfish/Err.h"
+#include "Clownfish/Util/Memory.h"
+#include "Clownfish/Util/StringHelper.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);
+
+String*
+Str_new(size_t size) {
+    String *self = (String*)VTable_Make_Obj(STRING);
+    return Str_init(self, size);
+}
+
+String*
+Str_init(String *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;
+}
+
+String*
+Str_new_from_utf8(const char *ptr, size_t size) {
+    if (!StrHelp_utf8_valid(ptr, size)) {
+        DIE_INVALID_UTF8(ptr, size);
+    }
+    return Str_new_from_trusted_utf8(ptr, size);
+}
+
+String*
+Str_new_from_trusted_utf8(const char *ptr, size_t size) {
+    String *self = (String*)VTable_Make_Obj(STRING);
+
+    // 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;
+}
+
+String*
+Str_new_steal_from_trusted_str(char *ptr, size_t size, size_t cap) {
+    String *self = (String*)VTable_Make_Obj(STRING);
+    return Str_init_steal_trusted_str(self, ptr, size, cap);
+}
+
+String*
+Str_init_steal_trusted_str(String *self, char *ptr, size_t size, size_t cap) {
+    self->ptr  = ptr;
+    self->size = size;
+    self->cap  = cap;
+    return self;
+}
+
+String*
+Str_new_steal_str(char *ptr, size_t size, size_t cap) {
+    if (!StrHelp_utf8_valid(ptr, size)) {
+        DIE_INVALID_UTF8(ptr, size);
+    }
+    return Str_new_steal_from_trusted_str(ptr, size, cap);
+}
+
+String*
+Str_newf(const char *pattern, ...) {
+    String *self = Str_new(strlen(pattern));
+    va_list args;
+    va_start(args, pattern);
+    Str_VCatF(self, pattern, args);
+    va_end(args);
+    return self;
+}
+
+void
+Str_Destroy_IMP(String *self) {
+    FREEMEM(self->ptr);
+    SUPER_DESTROY(self, STRING);
+}
+
+int32_t
+Str_Hash_Sum_IMP(String *self) {
+    uint32_t hashvalue = 5381;
+    StackString *iterator = SSTR_WRAP(self);
+
+    const ViewCB_Nibble_t nibble = METHOD_PTR(iterator->vtable,
+                                              CFISH_ViewCB_Nibble);
+    while (iterator->size) {
+        uint32_t code_point = (uint32_t)nibble((ViewCharBuf*)iterator);
+        hashvalue = ((hashvalue << 5) + hashvalue) ^ code_point;
+    }
+
+    return (int32_t) hashvalue;
+}
+
+static void
+S_grow(String *self, size_t size) {
+    if (size >= self->cap) {
+        Str_Grow(self, size);
+    }
+}
+
+char*
+Str_Grow_IMP(String *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
+Str_catf(String *self, const char *pattern, ...) {
+    va_list args;
+    va_start(args, pattern);
+    Str_VCatF(self, pattern, args);
+    va_end(args);
+}
+
+void
+Str_VCatF_IMP(String *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;
+            Str_Cat_Trusted_Str(self, pattern, size);
+            pattern = slice_end;
+        }
+
+        if (pattern < pattern_end) {
+            pattern++; // Move past '%'.
+
+            switch (*pattern) {
+                case '%': {
+                        Str_Cat_Trusted_Str(self, "%", 1);
+                    }
+                    break;
+                case 'o': {
+                        Obj *obj = va_arg(args, Obj*);
+                        if (!obj) {
+                            Str_Cat_Trusted_Str(self, "[NULL]", 6);
+                        }
+                        else if (Obj_Is_A(obj, STRING)) {
+                            Str_Cat(self, (String*)obj);
+                        }
+                        else {
+                            String *string = Obj_To_String(obj);
+                            Str_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);
+                        Str_Cat_Trusted_Str(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);
+                        Str_Cat_Trusted_Str(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);
+                            Str_Cat_Trusted_Str(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);
+                            Str_Cat_Trusted_Str(self, buf, size);
+                            pattern += 2;
+                        }
+                        else {
+                            S_die_invalid_pattern(pattern_start);
+                        }
+                    }
+                    break;
+                case 's': {
+                        char *string = va_arg(args, char*);
+                        if (string == NULL) {
+                            Str_Cat_Trusted_Str(self, "[NULL]", 6);
+                        }
+                        else {
+                            size_t size = strlen(string);
+                            if (StrHelp_utf8_valid(string, size)) {
+                                Str_Cat_Trusted_Str(self, string, size);
+                            }
+                            else {
+                                Str_Cat_Trusted_Str(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*
+Str_To_String_IMP(String *self) {
+    return Str_new_from_trusted_utf8(self->ptr, self->size);
+}
+
+void
+Str_Cat_Char_IMP(String *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';
+}
+
+int32_t
+Str_Swap_Chars_IMP(String *self, uint32_t match, uint32_t replacement) {
+    int32_t num_swapped = 0;
+
+    if (match > 127) {
+        THROW(ERR, "match point too high: %u32", match);
+    }
+    else if (replacement > 127) {
+        THROW(ERR, "replacement code point too high: %u32", replacement);
+    }
+    else {
+        char *ptr = self->ptr;
+        char *const limit = ptr + self->size;
+        for (; ptr < limit; ptr++) {
+            if (*ptr == (char)match) {
+                *ptr = (char)replacement;
+                num_swapped++;
+            }
+        }
+    }
+
+    return num_swapped;
+}
+
+int64_t
+Str_To_I64_IMP(String *self) {
+    return Str_BaseX_To_I64(self, 10);
+}
+
+int64_t
+Str_BaseX_To_I64_IMP(String *self, uint32_t base) {
+    StackString *iterator = SSTR_WRAP(self);
+    int64_t retval = 0;
+    bool is_negative = false;
+
+    // Advance past minus sign.
+    if (SStr_Code_Point_At(iterator, 0) == '-') {
+        SStr_Nibble(iterator);
+        is_negative = true;
+    }
+
+    // Accumulate.
+    while (iterator->size) {
+        int32_t code_point = SStr_Nibble(iterator);
+        if (isalnum(code_point)) {
+            int32_t addend = isdigit(code_point)
+                             ? code_point - '0'
+                             : tolower(code_point) - 'a' + 10;
+            if (addend > (int32_t)base) { break; }
+            retval *= base;
+            retval += addend;
+        }
+        else {
+            break;
+        }
+    }
+
+    // Apply minus sign.
+    if (is_negative) { retval = 0 - retval; }
+
+    return retval;
+}
+
+static double
+S_safe_to_f64(String *self) {
+    size_t amount = self->size < 511 ? self->size : 511;
+    char buf[512];
+    memcpy(buf, self->ptr, amount);
+    buf[amount] = 0; // NULL-terminate.
+    return strtod(buf, NULL);
+}
+
+double
+Str_To_F64_IMP(String *self) {
+    char   *end;
+    double  value    = strtod(self->ptr, &end);
+    size_t  consumed = end - self->ptr;
+    if (consumed > self->size) { // strtod overran
+        value = S_safe_to_f64(self);
+    }
+    return value;
+}
+
+String*
+Str_To_CB8_IMP(String *self) {
+    return Str_new_from_trusted_utf8(self->ptr, self->size);
+}
+
+String*
+Str_Clone_IMP(String *self) {
+    return Str_new_from_trusted_utf8(self->ptr, self->size);
+}
+
+void
+Str_Mimic_Str_IMP(String *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
+Str_Mimic_IMP(String *self, Obj *other) {
+    String *twin = (String*)CERTIFY(other, STRING);
+    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';
+}
+
+void
+Str_Cat_Str_IMP(String *self, const char* ptr, size_t size) {
+    if (!StrHelp_utf8_valid(ptr, size)) {
+        DIE_INVALID_UTF8(ptr, size);
+    }
+    Str_Cat_Trusted_Str_IMP(self, ptr, size);
+}
+
+void
+Str_Cat_Trusted_Str_IMP(String *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
+Str_Cat_IMP(String *self, const String *other) {
+    const size_t new_size = self->size + other->size;
+    if (new_size >= self->cap) {
+        size_t amount = Memory_oversize(new_size, sizeof(char));
+        S_grow(self, amount);
+    }
+    memcpy((self->ptr + self->size), other->ptr, other->size);
+    self->size = new_size;
+    self->ptr[new_size] = '\0';
+}
+
+bool
+Str_Starts_With_IMP(String *self, const String *prefix) {
+    return Str_Starts_With_Str_IMP(self, prefix->ptr, prefix->size);
+}
+
+bool
+Str_Starts_With_Str_IMP(String *self, const char *prefix, size_t size) {
+    if (size <= self->size
+        && (memcmp(self->ptr, prefix, size) == 0)
+       ) {
+        return true;
+    }
+    else {
+        return false;
+    }
+}
+
+bool
+Str_Equals_IMP(String *self, Obj *other) {
+    String *const twin = (String*)other;
+    if (twin == self)              { return true; }
+    if (!Obj_Is_A(other, STRING)) { return false; }
+    return Str_Equals_Str_IMP(self, twin->ptr, twin->size);
+}
+
+int32_t
+Str_Compare_To_IMP(String *self, Obj *other) {
+    CERTIFY(other, STRING);
+    return Str_compare(&self, &other);
+}
+
+bool
+Str_Equals_Str_IMP(String *self, const char *ptr, size_t size) {
+    if (self->size != size) {
+        return false;
+    }
+    return (memcmp(self->ptr, ptr, self->size) == 0);
+}
+
+bool
+Str_Ends_With_IMP(String *self, const String *postfix) {
+    return Str_Ends_With_Str_IMP(self, postfix->ptr, postfix->size);
+}
+
+bool
+Str_Ends_With_Str_IMP(String *self, const char *postfix, size_t postfix_len) {
+    if (postfix_len <= self->size) {
+        char *start = self->ptr + self->size - postfix_len;
+        if (memcmp(start, postfix, postfix_len) == 0) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+int64_t
+Str_Find_IMP(String *self, const String *substring) {
+    return Str_Find_Str(self, substring->ptr, substring->size);
+}
+
+int64_t
+Str_Find_Str_IMP(String *self, const char *ptr, size_t size) {
+    StackString *iterator = SSTR_WRAP(self);
+    int64_t location = 0;
+
+    while (iterator->size) {
+        if (SStr_Starts_With_Str(iterator, ptr, size)) {
+            return location;
+        }
+        SStr_Nip(iterator, 1);
+        location++;
+    }
+
+    return -1;
+}
+
+uint32_t
+Str_Trim_IMP(String *self) {
+    return Str_Trim_Top(self) + Str_Trim_Tail(self);
+}
+
+uint32_t
+Str_Trim_Top_IMP(String *self) {
+    char     *ptr   = self->ptr;
+    char     *end   = ptr + self->size;
+    uint32_t  count = 0;
+
+    while (ptr < end) {
+        uint32_t code_point = StrHelp_decode_utf8_char(ptr);
+        if (!StrHelp_is_whitespace(code_point)) { break; }
+        ptr += StrHelp_UTF8_COUNT[*(uint8_t*)ptr];
+        count++;
+    }
+    if (ptr > end) {
+        DIE_INVALID_UTF8(self->ptr, self->size);
+    }
+
+    if (count) {
+        // Copy string backwards.
+        self->size = end - ptr;
+        memmove(self->ptr, ptr, self->size);
+    }
+
+    return count;
+}
+
+uint32_t
+Str_Trim_Tail_IMP(String *self) {
+    uint32_t      count    = 0;
+    char *const   top      = self->ptr;
+    const char   *ptr      = top + self->size;
+    size_t        new_size = self->size;
+
+    while (NULL != (ptr = StrHelp_back_utf8_char(ptr, top))) {
+        uint32_t code_point = StrHelp_decode_utf8_char(ptr);
+        if (!StrHelp_is_whitespace(code_point)) { break; }
+        new_size = ptr - top;
+        count++;
+    }
+    self->size = new_size;
+
+    return count;
+}
+
+size_t
+Str_Length_IMP(String *self) {
+    size_t  len  = 0;
+    char   *ptr  = self->ptr;
+    char   *end  = ptr + self->size;
+    while (ptr < end) {
+        ptr += StrHelp_UTF8_COUNT[*(uint8_t*)ptr];
+        len++;
+    }
+    if (ptr != end) {
+        DIE_INVALID_UTF8(self->ptr, self->size);
+    }
+    return len;
+}
+
+size_t
+Str_Truncate_IMP(String *self, size_t count) {
+    uint32_t num_code_points;
+    StackString *iterator = SSTR_WRAP(self);
+    num_code_points = SStr_Nip(iterator, count);
+    self->size -= iterator->size;
+    return num_code_points;
+}
+
+uint32_t
+Str_Code_Point_At_IMP(String *self, size_t tick) {
+    size_t count = 0;
+    char *ptr = self->ptr;
+    char *const end = ptr + self->size;
+
+    for (; ptr < end; ptr += StrHelp_UTF8_COUNT[*(uint8_t*)ptr]) {
+        if (count == tick) {
+            if (ptr > end) {
+                DIE_INVALID_UTF8(self->ptr, self->size);
+            }
+            return StrHelp_decode_utf8_char(ptr);
+        }
+        count++;
+    }
+
+    return 0;
+}
+
+uint32_t
+Str_Code_Point_From_IMP(String *self, size_t tick) {
+    size_t      count = 0;
+    char       *top   = self->ptr;
+    const char *ptr   = top + self->size;
+
+    for (count = 0; count < tick; count++) {
+        if (NULL == (ptr = StrHelp_back_utf8_char(ptr, top))) { return 0; }
+    }
+    return StrHelp_decode_utf8_char(ptr);
+}
+
+String*
+Str_SubString_IMP(String *self, size_t offset, size_t len) {
+    StackString *iterator = SSTR_WRAP(self);
+    char *sub_start;
+    size_t byte_len;
+
+    SStr_Nip(iterator, offset);
+    sub_start = iterator->ptr;
+    SStr_Nip(iterator, len);
+    byte_len = iterator->ptr - sub_start;
+
+    return Str_new_from_trusted_utf8(sub_start, byte_len);
+}
+
+int
+Str_compare(const void *va, const void *vb) {
+    const String *a = *(const String**)va;
+    const String *b = *(const String**)vb;
+    StackString *iterator_a = SSTR_WRAP(a);
+    StackString *iterator_b = SSTR_WRAP(b);
+    while (iterator_a->size && iterator_b->size) {
+        int32_t code_point_a = SStr_Nibble(iterator_a);
+        int32_t code_point_b = SStr_Nibble(iterator_b);
+        const int32_t comparison = code_point_a - code_point_b;
+        if (comparison != 0) { return comparison; }
+    }
+    if (iterator_a->size != iterator_b->size) {
+        return iterator_a->size < iterator_b->size ? -1 : 1;
+    }
+    return 0;
+}
+
+bool
+Str_less_than(const void *va, const void *vb) {
+    return Str_compare(va, vb) < 0 ? 1 : 0;
+}
+
+void
+Str_Set_Size_IMP(String *self, size_t size) {
+    self->size = size;
+}
+
+size_t
+Str_Get_Size_IMP(String *self) {
+    return self->size;
+}
+
+uint8_t*
+Str_Get_Ptr8_IMP(String *self) {
+    return (uint8_t*)self->ptr;
+}
+
+/*****************************************************************/
+
+ViewCharBuf*
+ViewCB_new_from_utf8(const char *utf8, size_t size) {
+    if (!StrHelp_utf8_valid(utf8, size)) {
+        DIE_INVALID_UTF8(utf8, size);
+    }
+    return ViewCB_new_from_trusted_utf8(utf8, size);
+}
+
+ViewCharBuf*
+ViewCB_new_from_trusted_utf8(const char *utf8, size_t size) {
+    ViewCharBuf *self = (ViewCharBuf*)VTable_Make_Obj(VIEWCHARBUF);
+    return ViewCB_init(self, utf8, size);
+}
+
+ViewCharBuf*
+ViewCB_init(ViewCharBuf *self, const char *utf8, size_t size) {
+    self->ptr  = (char*)utf8;
+    self->size = size;
+    self->cap  = 0;
+    return self;
+}
+
+void
+ViewCB_Destroy_IMP(ViewCharBuf *self) {
+    // Note that we do not free self->ptr, and that we invoke the
+    // SUPER_DESTROY with STRING instead of VIEWCHARBUF.
+    SUPER_DESTROY(self, STRING);
+}
+
+void
+ViewCB_Assign_IMP(ViewCharBuf *self, const String *other) {
+    self->ptr  = other->ptr;
+    self->size = other->size;
+}
+
+void
+ViewCB_Assign_Str_IMP(ViewCharBuf *self, const char *utf8, size_t size) {
+    if (!StrHelp_utf8_valid(utf8, size)) {
+        DIE_INVALID_UTF8(utf8, size);
+    }
+    self->ptr  = (char*)utf8;
+    self->size = size;
+}
+
+void
+ViewCB_Assign_Trusted_Str_IMP(ViewCharBuf *self, const char *utf8, size_t size) {
+    self->ptr  = (char*)utf8;
+    self->size = size;
+}
+
+uint32_t
+ViewCB_Trim_Top_IMP(ViewCharBuf *self) {
+    uint32_t  count = 0;
+    char     *ptr   = self->ptr;
+    char     *end   = ptr + self->size;
+
+    while (ptr < end) {
+        uint32_t code_point = StrHelp_decode_utf8_char(ptr);
+        if (!StrHelp_is_whitespace(code_point)) { break; }
+        ptr += StrHelp_UTF8_COUNT[*(uint8_t*)ptr];
+        count++;
+    }
+
+    if (count) {
+        if (ptr > end) {
+            DIE_INVALID_UTF8(self->ptr, self->size);
+        }
+        self->size = end - ptr;
+        self->ptr  = ptr;
+    }
+
+    return count;
+}
+
+size_t
+ViewCB_Nip_IMP(ViewCharBuf *self, size_t count) {
+    size_t  num_nipped;
+    char   *ptr = self->ptr;
+    char   *end = ptr + self->size;
+    for (num_nipped = 0;
+         ptr < end && count--;
+         ptr += StrHelp_UTF8_COUNT[*(uint8_t*)ptr]
+        ) {
+        num_nipped++;
+    }
+    if (ptr > end) {
+        DIE_INVALID_UTF8(self->ptr, self->size);
+    }
+    self->size = end - ptr;
+    self->ptr  = ptr;
+    return num_nipped;
+}
+
+int32_t
+ViewCB_Nibble_IMP(ViewCharBuf *self) {
+    if (self->size == 0) {
+        return 0;
+    }
+    else {
+        int32_t retval = (int32_t)StrHelp_decode_utf8_char(self->ptr);
+        size_t consumed = StrHelp_UTF8_COUNT[*(uint8_t*)self->ptr];
+        if (consumed > self->size) {
+            DIE_INVALID_UTF8(self->ptr, self->size);
+        }
+        self->ptr  += consumed;
+        self->size -= consumed;
+        return retval;
+    }
+}
+
+size_t
+ViewCB_Chop_IMP(ViewCharBuf *self, size_t count) {
+    size_t      num_chopped = 0;
+    char       *top         = self->ptr;
+    const char *ptr         = top + self->size;
+    for (num_chopped = 0; num_chopped < count; num_chopped++) {
+        const char *end = ptr;
+        if (NULL == (ptr = StrHelp_back_utf8_char(ptr, top))) { break; }
+        self->size -= (end - ptr);
+    }
+    return num_chopped;
+}
+
+char*
+ViewCB_Grow_IMP(ViewCharBuf *self, size_t size) {
+    UNUSED_VAR(size);
+    THROW(ERR, "Can't grow a ViewCharBuf ('%o')", self);
+    UNREACHABLE_RETURN(char*);
+}
+
+/*****************************************************************/
+
+StackString*
+SStr_new(void *allocation) {
+    static char empty_string[] = "";
+    StackString *self
+        = (StackString*)VTable_Init_Obj(STACKSTRING, allocation);
+    self->cap  = 0;
+    self->size = 0;
+    self->ptr  = empty_string;
+    return self;
+}
+
+StackString*
+SStr_newf(void *allocation, size_t alloc_size, const char *pattern, ...) {
+    StackString *self
+        = (StackString*)VTable_Init_Obj(STACKSTRING, allocation);
+    self->cap  = alloc_size - sizeof(StackString);
+    self->size = 0;
+    self->ptr  = ((char*)allocation) + sizeof(StackString);
+
+    va_list args;
+    va_start(args, pattern);
+    SStr_VCatF(self, pattern, args);
+    va_end(args);
+
+    return self;
+}
+
+StackString*
+SStr_wrap_str(void *allocation, const char *ptr, size_t size) {
+    StackString *self
+        = (StackString*)VTable_Init_Obj(STACKSTRING, allocation);
+    self->cap  = 0;
+    self->size = size;
+    self->ptr  = (char*)ptr;
+    return self;
+}
+
+StackString*
+SStr_wrap(void *allocation, const String *source) {
+    return SStr_wrap_str(allocation, source->ptr, source->size);
+}
+
+size_t
+SStr_size() {
+    return sizeof(StackString);
+}
+
+void
+SStr_Destroy_IMP(StackString *self) {
+    THROW(ERR, "Can't destroy a StackString ('%o')", self);
+}
+
+

http://git-wip-us.apache.org/repos/asf/lucy/blob/2c3dbf15/clownfish/runtime/core/Clownfish/String.cfh
----------------------------------------------------------------------
diff --git a/clownfish/runtime/core/Clownfish/String.cfh b/clownfish/runtime/core/Clownfish/String.cfh
new file mode 100644
index 0000000..2cddf4a
--- /dev/null
+++ b/clownfish/runtime/core/Clownfish/String.cfh
@@ -0,0 +1,392 @@
+/* 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::String cnick Str
+    inherits Clownfish::Obj {
+
+    char    *ptr;
+    size_t   size;
+    size_t   cap;  /* allocated bytes, including terminating null */
+
+    inert incremented String*
+    new(size_t size);
+
+    inert String*
+    init(String *self, size_t size);
+
+    /** Return a new String which holds a copy of the passed-in string.
+     * Check for UTF-8 validity.
+     */
+    inert incremented String*
+    new_from_utf8(const char *utf8, size_t size);
+
+    /** Return a new String which holds a copy of the passed-in string.  No
+     * validity checking is performed.
+     */
+    inert incremented String*
+    new_from_trusted_utf8(const char *utf8, size_t size);
+
+    /** Return a pointer to a new String which assumes ownership of the
+     * passed-in string.  Check validity of supplied UTF-8.
+     */
+    inert incremented String*
+    new_steal_str(char *ptr, size_t size, size_t cap);
+
+    /** Return a pointer to a new String which assumes ownership of the
+     * passed-in string.  Do not check validity of supplied UTF-8.
+     */
+    inert incremented String*
+    new_steal_from_trusted_str(char *ptr, size_t size, size_t cap);
+
+    /** Initialize the String using the passed-in string.  Do not check
+     * validity of supplied UTF-8.
+     */
+    public inert String*
+    init_steal_trusted_str(decremented String *self, char *ptr,
+                           size_t size, size_t cap);
+
+    /** Return a pointer to a new String 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 String*
+    newf(const char *pattern, ...);
+
+    /** Perform lexical comparison of two Strings, with level of indirection
+     * set to please qsort and friends.
+     */
+    inert int
+    compare(const void *va, const void *vb);
+
+    /** Perform lexical comparison of two Strings, with level of indirection
+     * set to please qsort and friends, and return true if <code>a</code> is
+     * less than <code>b</code>.
+     */
+    inert bool
+    less_than(const void *va, const void *vb);
+
+    public void
+    Mimic(String *self, Obj *other);
+
+    void
+    Mimic_Str(String *self, const char *ptr, size_t size);
+
+    /** Concatenate the passed-in string onto the end of the String.
+     */
+    void
+    Cat_Str(String *self, const char *ptr, size_t size);
+
+    /** Concatenate the contents of <code>other</code> onto the end of the
+     * caller.
+     */
+    void
+    Cat(String *self, const String *other);
+
+    /** 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 Strings, are printed via
+     * %o (which invokes Obj_To_String()).
+     */
+    void
+    VCatF(String *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(String *self, const char *pattern, ...);
+
+    /** Concatenate one Unicode character onto the end of the String.
+     */
+    void
+    Cat_Char(String *self, uint32_t code_point);
+
+    /** Replace all instances of one character for the other.  For now, both
+     * the source and replacement code points must be ASCII.
+     */
+    int32_t
+    Swap_Chars(String *self, uint32_t match, uint32_t replacement);
+
+    public int64_t
+    To_I64(String *self);
+
+    /** Extract a 64-bit integer from a variable-base stringified version.
+     */
+    int64_t
+    BaseX_To_I64(String *self, uint32_t base);
+
+    public double
+    To_F64(String *self);
+
+    /** Assign more memory to the String, 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(String *self, size_t size);
+
+    /** Test whether the String starts with the content of another.
+     */
+    bool
+    Starts_With(String *self, const String *prefix);
+
+    /** Test whether the String starts with the passed-in string.
+     */
+    bool
+    Starts_With_Str(String *self, const char *prefix, size_t size);
+
+    /** Test whether the String ends with the content of another.
+     */
+    bool
+    Ends_With(String *self, const String *postfix);
+
+    /** Test whether the String ends with the passed-in string.
+     */
+    bool
+    Ends_With_Str(String *self, const char *postfix, size_t size);
+
+    /** Return the location of the substring within the String (measured in
+     * code points), or -1 if the substring does not match.
+     */
+    int64_t
+    Find(String *self, const String *substring);
+
+    int64_t
+    Find_Str(String *self, const char *ptr, size_t size);
+
+    /** Test whether the String matches the passed-in string.
+     */
+    bool
+    Equals_Str(String *self, const char *ptr, size_t size);
+
+    /** Return the number of Unicode code points in the object's string.
+     */
+    size_t
+    Length(String *self);
+
+    /** Set the String's <code>size</code> attribute.
+     */
+    void
+    Set_Size(String *self, size_t size);
+
+    /** Get the String's <code>size</code> attribute.
+     */
+    size_t
+    Get_Size(String *self);
+
+    /** Return the internal backing array for the String if its internal
+     * encoding is UTF-8.  If it is not encoded as UTF-8 throw an exception.
+     */
+    uint8_t*
+    Get_Ptr8(String *self);
+
+    /** Return a fresh copy of the string data in a String with an internal
+     * encoding of UTF-8.
+     */
+    String*
+    To_CB8(String *self);
+
+    public incremented String*
+    Clone(String *self);
+
+    public void
+    Destroy(String *self);
+
+    public bool
+    Equals(String *self, Obj *other);
+
+    public int32_t
+    Compare_To(String *self, Obj *other);
+
+    public int32_t
+    Hash_Sum(String *self);
+
+    public incremented String*
+    To_String(String *self);
+
+    /** Remove Unicode whitespace characters from both top and tail.
+     */
+    uint32_t
+    Trim(String *self);
+
+    /** Remove leading Unicode whitespace.
+     */
+    uint32_t
+    Trim_Top(String *self);
+
+    /** Remove trailing Unicode whitespace.
+     */
+    uint32_t
+    Trim_Tail(String *self);
+
+    /** Truncate the String so that it contains no more than
+     * <code>count</code>characters.
+     *
+     * @param count Maximum new length, in Unicode code points.
+     * @return The number of code points left in the string after truncation.
+     */
+    size_t
+    Truncate(String *self, size_t count);
+
+    /** Return the Unicode code point at the specified number of code points
+     * in.  Return 0 if the string length is exceeded.  (XXX It would be
+     * better to throw an exception, but that's not practical with UTF-8 and
+     * no cached length.)
+     */
+    uint32_t
+    Code_Point_At(String *self, size_t tick);
+
+    /** Return the Unicode code point at the specified number of code points
+     * counted backwards from the end of the string.  Return 0 if outside the
+     * string.
+     */
+    uint32_t
+    Code_Point_From(String *self, size_t tick);
+
+    /** Return a newly allocated String containing a copy of the indicated
+     * substring.
+     * @param offset Offset from the top, in code points.
+     * @param len The desired length of the substring, in code points.
+     */
+    incremented String*
+    SubString(String *self, size_t offset, size_t len);
+
+    /** Concatenate the supplied text onto the end of the String.  Don't
+     * check for UTF-8 validity.
+     */
+    void
+    Cat_Trusted_Str(String *self, const char *ptr, size_t size);
+}
+
+class Clownfish::ViewCharBuf cnick ViewCB
+    inherits Clownfish::String {
+
+    inert incremented ViewCharBuf*
+    new_from_utf8(const char *utf8, size_t size);
+
+    inert incremented ViewCharBuf*
+    new_from_trusted_utf8(const char *utf8, size_t size);
+
+    inert ViewCharBuf*
+    init(ViewCharBuf *self, const char *utf8, size_t size);
+
+    void
+    Assign(ViewCharBuf *self, const String *other);
+
+    void
+    Assign_Str(ViewCharBuf *self, const char *utf8, size_t size);
+
+    void
+    Assign_Trusted_Str(ViewCharBuf *self, const char *utf8, size_t size);
+
+    uint32_t
+    Trim_Top(ViewCharBuf *self);
+
+    /** Remove characters (measured in code points) from the top of the
+     * ViewCharBuf.  Returns the number nipped.
+     */
+    size_t
+    Nip(ViewCharBuf *self, size_t count);
+
+    /** Remove one character from the top of the ViewCharBuf.  Returns the
+     * code point, or 0 if the string was empty.
+     */
+    int32_t
+    Nibble(ViewCharBuf *self);
+
+    /** Remove characters (measured in code points) from the end of the
+     * ViewCharBuf.  Returns the number chopped.
+     */
+    size_t
+    Chop(ViewCharBuf *self, size_t count);
+
+    /** Throws an error. */
+    char*
+    Grow(ViewCharBuf *self, size_t size);
+
+    public void
+    Destroy(ViewCharBuf *self);
+}
+
+class Clownfish::StackString cnick SStr
+    inherits Clownfish::ViewCharBuf {
+
+    /** Return a StackString with a blank string.
+     */
+    inert incremented StackString*
+    new(void *allocation);
+
+    /**
+     * @param allocation A single block of memory which will be used for both
+     * the StackString object and its buffer.
+     * @param alloc_size The size of the allocation.
+     * @param pattern A format pattern.
+     */
+    inert incremented StackString*
+    newf(void *allocation, size_t alloc_size, const char *pattern, ...);
+
+    inert incremented StackString*
+    wrap(void *allocation, const String *source);
+
+    inert incremented StackString*
+    wrap_str(void *allocation, const char *ptr, size_t size);
+
+    /** Return the size for a StackString struct.
+     */
+    inert size_t
+    size();
+
+    /** Throws an error.
+     */
+    public void
+    Destroy(StackString *self);
+}
+
+__C__
+
+#define CFISH_SStr_BLANK() cfish_SStr_new(cfish_alloca(cfish_SStr_size()))
+
+#define CFISH_SStr_WRAP(source) \
+    cfish_SStr_wrap(cfish_alloca(cfish_SStr_size()), source)
+
+#define CFISH_SStr_WRAP_STR(ptr, size) \
+    cfish_SStr_wrap_str(cfish_alloca(cfish_SStr_size()), ptr, size)
+
+#ifdef CFISH_USE_SHORT_NAMES
+  #define SStr_BLANK             CFISH_SStr_BLANK
+  #define SSTR_WRAP              CFISH_SStr_WRAP
+  #define SSTR_WRAP_STR          CFISH_SStr_WRAP_STR
+#endif
+__END_C__
+
+

http://git-wip-us.apache.org/repos/asf/lucy/blob/2c3dbf15/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
index 50e3c23..4ea9429 100644
--- a/clownfish/runtime/core/Clownfish/Test/TestCharBuf.c
+++ b/clownfish/runtime/core/Clownfish/Test/TestCharBuf.c
@@ -25,7 +25,7 @@
 
 #include "Clownfish/Test/TestCharBuf.h"
 
-#include "Clownfish/CharBuf.h"
+#include "Clownfish/String.h"
 #include "Clownfish/Num.h"
 #include "Clownfish/Test.h"
 #include "Clownfish/TestHarness/TestBatchRunner.h"
@@ -40,33 +40,33 @@ TestCB_new() {
     return (TestCharBuf*)VTable_Make_Obj(TESTCHARBUF);
 }
 
-static CharBuf*
+static String*
 S_get_cb(const char *string) {
-    return CB_new_from_utf8(string, strlen(string));
+    return Str_new_from_utf8(string, strlen(string));
 }
 
 static void
 test_Cat(TestBatchRunner *runner) {
-    CharBuf *wanted = CB_newf("a%s", smiley);
-    CharBuf *got    = S_get_cb("");
+    String *wanted = Str_newf("a%s", smiley);
+    String *got    = S_get_cb("");
 
-    CB_Cat(got, wanted);
-    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "Cat");
+    Str_Cat(got, wanted);
+    TEST_TRUE(runner, Str_Equals(wanted, (Obj*)got), "Cat");
     DECREF(got);
 
     got = S_get_cb("a");
-    CB_Cat_Char(got, 0x263A);
-    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "Cat_Char");
+    Str_Cat_Char(got, 0x263A);
+    TEST_TRUE(runner, Str_Equals(wanted, (Obj*)got), "Cat_Char");
     DECREF(got);
 
     got = S_get_cb("a");
-    CB_Cat_Str(got, smiley, smiley_len);
-    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "Cat_Str");
+    Str_Cat_Str(got, smiley, smiley_len);
+    TEST_TRUE(runner, Str_Equals(wanted, (Obj*)got), "Cat_Str");
     DECREF(got);
 
     got = S_get_cb("a");
-    CB_Cat_Trusted_Str(got, smiley, smiley_len);
-    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "Cat_Trusted_Str");
+    Str_Cat_Trusted_Str(got, smiley, smiley_len);
+    TEST_TRUE(runner, Str_Equals(wanted, (Obj*)got), "Cat_Trusted_Str");
     DECREF(got);
 
     DECREF(wanted);
@@ -74,20 +74,20 @@ test_Cat(TestBatchRunner *runner) {
 
 static void
 test_Mimic_and_Clone(TestBatchRunner *runner) {
-    CharBuf *wanted = S_get_cb("foo");
-    CharBuf *got    = S_get_cb("bar");
+    String *wanted = S_get_cb("foo");
+    String *got    = S_get_cb("bar");
 
-    CB_Mimic(got, (Obj*)wanted);
-    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "Mimic");
+    Str_Mimic(got, (Obj*)wanted);
+    TEST_TRUE(runner, Str_Equals(wanted, (Obj*)got), "Mimic");
     DECREF(got);
 
     got = S_get_cb("bar");
-    CB_Mimic_Str(got, "foo", 3);
-    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "Mimic_Str");
+    Str_Mimic_Str(got, "foo", 3);
+    TEST_TRUE(runner, Str_Equals(wanted, (Obj*)got), "Mimic_Str");
     DECREF(got);
 
-    got = CB_Clone(wanted);
-    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "Clone");
+    got = Str_Clone(wanted);
+    TEST_TRUE(runner, Str_Equals(wanted, (Obj*)got), "Clone");
     DECREF(got);
 
     DECREF(wanted);
@@ -95,25 +95,25 @@ test_Mimic_and_Clone(TestBatchRunner *runner) {
 
 static void
 test_Find(TestBatchRunner *runner) {
-    CharBuf *string;
-    CharBuf *substring = S_get_cb("foo");
+    String *string;
+    String *substring = S_get_cb("foo");
 
     string = S_get_cb("");
-    TEST_TRUE(runner, CB_Find(string, substring) == -1, "Not in empty string");
+    TEST_TRUE(runner, Str_Find(string, substring) == -1, "Not in empty string");
     DECREF(string);
 
     string = S_get_cb("foo");
-    TEST_TRUE(runner, CB_Find(string, substring) == 0, "Find complete string");
+    TEST_TRUE(runner, Str_Find(string, substring) == 0, "Find complete string");
     DECREF(string);
 
     string = S_get_cb("afoo");
-    TEST_TRUE(runner, CB_Find(string, substring) == 1, "Find after first");
-    CB_Set_Size(string, 3);
-    TEST_TRUE(runner, CB_Find(string, substring) == -1, "Don't overrun");
+    TEST_TRUE(runner, Str_Find(string, substring) == 1, "Find after first");
+    Str_Set_Size(string, 3);
+    TEST_TRUE(runner, Str_Find(string, substring) == -1, "Don't overrun");
     DECREF(string);
 
     string = S_get_cb("afood");
-    TEST_TRUE(runner, CB_Find(string, substring) == 1, "Find in middle");
+    TEST_TRUE(runner, Str_Find(string, substring) == 1, "Find in middle");
     DECREF(string);
 
     DECREF(substring);
@@ -123,14 +123,14 @@ static void
 test_Code_Point_At_and_From(TestBatchRunner *runner) {
     uint32_t code_points[] = { 'a', 0x263A, 0x263A, 'b', 0x263A, 'c' };
     uint32_t num_code_points = sizeof(code_points) / sizeof(uint32_t);
-    CharBuf *string = CB_newf("a%s%sb%sc", smiley, smiley, smiley);
+    String *string = Str_newf("a%s%sb%sc", smiley, smiley, smiley);
     uint32_t i;
 
     for (i = 0; i < num_code_points; i++) {
         uint32_t from = num_code_points - i - 1;
-        TEST_INT_EQ(runner, CB_Code_Point_At(string, i), code_points[i],
+        TEST_INT_EQ(runner, Str_Code_Point_At(string, i), code_points[i],
                     "Code_Point_At %ld", (long)i);
-        TEST_INT_EQ(runner, CB_Code_Point_At(string, from),
+        TEST_INT_EQ(runner, Str_Code_Point_At(string, from),
                     code_points[from], "Code_Point_From %ld", (long)from);
     }
 
@@ -139,10 +139,10 @@ test_Code_Point_At_and_From(TestBatchRunner *runner) {
 
 static void
 test_SubString(TestBatchRunner *runner) {
-    CharBuf *string = CB_newf("a%s%sb%sc", smiley, smiley, smiley);
-    CharBuf *wanted = CB_newf("%sb%s", smiley, smiley);
-    CharBuf *got = CB_SubString(string, 2, 3);
-    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "SubString");
+    String *string = Str_newf("a%s%sb%sc", smiley, smiley, smiley);
+    String *wanted = Str_newf("%sb%s", smiley, smiley);
+    String *got = Str_SubString(string, 2, 3);
+    TEST_TRUE(runner, Str_Equals(wanted, (Obj*)got), "SubString");
     DECREF(wanted);
     DECREF(got);
     DECREF(string);
@@ -150,23 +150,23 @@ test_SubString(TestBatchRunner *runner) {
 
 static void
 test_Nip_and_Chop(TestBatchRunner *runner) {
-    CharBuf *wanted;
-    CharBuf *string;
+    String *wanted;
+    String *string;
     StackString *got;
 
-    wanted = CB_newf("%sb%sc", smiley, smiley);
-    string = CB_newf("a%s%sb%sc", smiley, smiley, smiley);
+    wanted = Str_newf("%sb%sc", smiley, smiley);
+    string = Str_newf("a%s%sb%sc", smiley, smiley, smiley);
     got    = SSTR_WRAP(string);
     SStr_Nip(got, 2);
-    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "Nip");
+    TEST_TRUE(runner, Str_Equals(wanted, (Obj*)got), "Nip");
     DECREF(wanted);
     DECREF(string);
 
-    wanted = CB_newf("a%s%s", smiley, smiley);
-    string = CB_newf("a%s%sb%sc", smiley, smiley, smiley);
+    wanted = Str_newf("a%s%s", smiley, smiley);
+    string = Str_newf("a%s%sb%sc", smiley, smiley, smiley);
     got    = SSTR_WRAP(string);
     SStr_Chop(got, 3);
-    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "Chop");
+    TEST_TRUE(runner, Str_Equals(wanted, (Obj*)got), "Chop");
     DECREF(wanted);
     DECREF(string);
 }
@@ -174,10 +174,10 @@ test_Nip_and_Chop(TestBatchRunner *runner) {
 
 static void
 test_Truncate(TestBatchRunner *runner) {
-    CharBuf *wanted = CB_newf("a%s", smiley, smiley);
-    CharBuf *got    = CB_newf("a%s%sb%sc", smiley, smiley, smiley);
-    CB_Truncate(got, 2);
-    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "Truncate");
+    String *wanted = Str_newf("a%s", smiley, smiley);
+    String *got    = Str_newf("a%s%sb%sc", smiley, smiley, smiley);
+    Str_Truncate(got, 2);
+    TEST_TRUE(runner, Str_Equals(wanted, (Obj*)got), "Truncate");
     DECREF(wanted);
     DECREF(got);
 }
@@ -192,31 +192,31 @@ test_Trim(TestBatchRunner *runner) {
     };
     uint32_t num_spaces = sizeof(spaces) / sizeof(uint32_t);
     uint32_t i;
-    CharBuf *got = CB_new(0);
+    String *got = Str_new(0);
 
     // Surround a smiley with lots of whitespace.
-    for (i = 0; i < num_spaces; i++) { CB_Cat_Char(got, spaces[i]); }
-    CB_Cat_Char(got, 0x263A);
-    for (i = 0; i < num_spaces; i++) { CB_Cat_Char(got, spaces[i]); }
+    for (i = 0; i < num_spaces; i++) { Str_Cat_Char(got, spaces[i]); }
+    Str_Cat_Char(got, 0x263A);
+    for (i = 0; i < num_spaces; i++) { Str_Cat_Char(got, spaces[i]); }
 
-    TEST_TRUE(runner, CB_Trim_Top(got), "Trim_Top returns true on success");
-    TEST_FALSE(runner, CB_Trim_Top(got),
+    TEST_TRUE(runner, Str_Trim_Top(got), "Trim_Top returns true on success");
+    TEST_FALSE(runner, Str_Trim_Top(got),
                "Trim_Top returns false on failure");
-    TEST_TRUE(runner, CB_Trim_Tail(got), "Trim_Tail returns true on success");
-    TEST_FALSE(runner, CB_Trim_Tail(got),
+    TEST_TRUE(runner, Str_Trim_Tail(got), "Trim_Tail returns true on success");
+    TEST_FALSE(runner, Str_Trim_Tail(got),
                "Trim_Tail returns false on failure");
-    TEST_TRUE(runner, CB_Equals_Str(got, smiley, smiley_len),
+    TEST_TRUE(runner, Str_Equals_Str(got, smiley, smiley_len),
               "Trim_Top and Trim_Tail worked");
 
     // Build the spacey smiley again.
-    CB_Truncate(got, 0);
-    for (i = 0; i < num_spaces; i++) { CB_Cat_Char(got, spaces[i]); }
-    CB_Cat_Char(got, 0x263A);
-    for (i = 0; i < num_spaces; i++) { CB_Cat_Char(got, spaces[i]); }
-
-    TEST_TRUE(runner, CB_Trim(got), "Trim returns true on success");
-    TEST_FALSE(runner, CB_Trim(got), "Trim returns false on failure");
-    TEST_TRUE(runner, CB_Equals_Str(got, smiley, smiley_len),
+    Str_Truncate(got, 0);
+    for (i = 0; i < num_spaces; i++) { Str_Cat_Char(got, spaces[i]); }
+    Str_Cat_Char(got, 0x263A);
+    for (i = 0; i < num_spaces; i++) { Str_Cat_Char(got, spaces[i]); }
+
+    TEST_TRUE(runner, Str_Trim(got), "Trim returns true on success");
+    TEST_FALSE(runner, Str_Trim(got), "Trim returns false on failure");
+    TEST_TRUE(runner, Str_Equals_Str(got, smiley, smiley_len),
               "Trim worked");
 
     DECREF(got);
@@ -224,24 +224,24 @@ test_Trim(TestBatchRunner *runner) {
 
 static void
 test_To_F64(TestBatchRunner *runner) {
-    CharBuf *charbuf;
+    String *charbuf;
 
     charbuf = S_get_cb("1.5");
-    double difference = 1.5 - CB_To_F64(charbuf);
+    double difference = 1.5 - Str_To_F64(charbuf);
     if (difference < 0) { difference = 0 - difference; }
     TEST_TRUE(runner, difference < 0.001, "To_F64");
     DECREF(charbuf);
 
     charbuf = S_get_cb("-1.5");
-    difference = 1.5 + CB_To_F64(charbuf);
+    difference = 1.5 + Str_To_F64(charbuf);
     if (difference < 0) { difference = 0 - difference; }
     TEST_TRUE(runner, difference < 0.001, "To_F64 negative");
     DECREF(charbuf);
 
     charbuf = S_get_cb("1.59");
-    double value_full = CB_To_F64(charbuf);
-    CB_Set_Size(charbuf, 3);
-    double value_short = CB_To_F64(charbuf);
+    double value_full = Str_To_F64(charbuf);
+    Str_Set_Size(charbuf, 3);
+    double value_short = Str_To_F64(charbuf);
     TEST_TRUE(runner, value_short < value_full,
               "TO_F64 doesn't run past end of string");
     DECREF(charbuf);
@@ -249,45 +249,45 @@ test_To_F64(TestBatchRunner *runner) {
 
 static void
 test_To_I64(TestBatchRunner *runner) {
-    CharBuf *charbuf;
+    String *charbuf;
 
     charbuf = S_get_cb("10");
-    TEST_TRUE(runner, CB_To_I64(charbuf) == 10, "To_I64");
+    TEST_TRUE(runner, Str_To_I64(charbuf) == 10, "To_I64");
     DECREF(charbuf);
 
     charbuf = S_get_cb("-10");
-    TEST_TRUE(runner, CB_To_I64(charbuf) == -10, "To_I64 negative");
+    TEST_TRUE(runner, Str_To_I64(charbuf) == -10, "To_I64 negative");
     DECREF(charbuf);
 }
 
 
 static void
 test_vcatf_s(TestBatchRunner *runner) {
-    CharBuf *wanted = S_get_cb("foo bar bizzle baz");
-    CharBuf *got = S_get_cb("foo ");
-    CB_catf(got, "bar %s baz", "bizzle");
-    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "%%s");
+    String *wanted = S_get_cb("foo bar bizzle baz");
+    String *got = S_get_cb("foo ");
+    Str_catf(got, "bar %s baz", "bizzle");
+    TEST_TRUE(runner, Str_Equals(wanted, (Obj*)got), "%%s");
     DECREF(wanted);
     DECREF(got);
 }
 
 static void
 test_vcatf_null_string(TestBatchRunner *runner) {
-    CharBuf *wanted = S_get_cb("foo bar [NULL] baz");
-    CharBuf *got = S_get_cb("foo ");
-    CB_catf(got, "bar %s baz", NULL);
-    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "%%s NULL");
+    String *wanted = S_get_cb("foo bar [NULL] baz");
+    String *got = S_get_cb("foo ");
+    Str_catf(got, "bar %s baz", NULL);
+    TEST_TRUE(runner, Str_Equals(wanted, (Obj*)got), "%%s NULL");
     DECREF(wanted);
     DECREF(got);
 }
 
 static void
 test_vcatf_cb(TestBatchRunner *runner) {
-    CharBuf *wanted = S_get_cb("foo bar ZEKE baz");
-    CharBuf *catworthy = S_get_cb("ZEKE");
-    CharBuf *got = S_get_cb("foo ");
-    CB_catf(got, "bar %o baz", catworthy);
-    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "%%o CharBuf");
+    String *wanted = S_get_cb("foo bar ZEKE baz");
+    String *catworthy = S_get_cb("ZEKE");
+    String *got = S_get_cb("foo ");
+    Str_catf(got, "bar %o baz", catworthy);
+    TEST_TRUE(runner, Str_Equals(wanted, (Obj*)got), "%%o String");
     DECREF(catworthy);
     DECREF(wanted);
     DECREF(got);
@@ -295,11 +295,11 @@ test_vcatf_cb(TestBatchRunner *runner) {
 
 static void
 test_vcatf_obj(TestBatchRunner *runner) {
-    CharBuf   *wanted = S_get_cb("ooga 20 booga");
+    String    *wanted = S_get_cb("ooga 20 booga");
     Integer32 *i32 = Int32_new(20);
-    CharBuf   *got = S_get_cb("ooga");
-    CB_catf(got, " %o booga", i32);
-    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "%%o Obj");
+    String    *got = S_get_cb("ooga");
+    Str_catf(got, " %o booga", i32);
+    TEST_TRUE(runner, Str_Equals(wanted, (Obj*)got), "%%o Obj");
     DECREF(i32);
     DECREF(wanted);
     DECREF(got);
@@ -307,108 +307,108 @@ test_vcatf_obj(TestBatchRunner *runner) {
 
 static void
 test_vcatf_null_obj(TestBatchRunner *runner) {
-    CharBuf *wanted = S_get_cb("foo bar [NULL] baz");
-    CharBuf *got = S_get_cb("foo ");
-    CB_catf(got, "bar %o baz", NULL);
-    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "%%o NULL");
+    String *wanted = S_get_cb("foo bar [NULL] baz");
+    String *got = S_get_cb("foo ");
+    Str_catf(got, "bar %o baz", NULL);
+    TEST_TRUE(runner, Str_Equals(wanted, (Obj*)got), "%%o NULL");
     DECREF(wanted);
     DECREF(got);
 }
 
 static void
 test_vcatf_i8(TestBatchRunner *runner) {
-    CharBuf *wanted = S_get_cb("foo bar -3 baz");
+    String *wanted = S_get_cb("foo bar -3 baz");
     int8_t num = -3;
-    CharBuf *got = S_get_cb("foo ");
-    CB_catf(got, "bar %i8 baz", num);
-    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "%%i8");
+    String *got = S_get_cb("foo ");
+    Str_catf(got, "bar %i8 baz", num);
+    TEST_TRUE(runner, Str_Equals(wanted, (Obj*)got), "%%i8");
     DECREF(wanted);
     DECREF(got);
 }
 
 static void
 test_vcatf_i32(TestBatchRunner *runner) {
-    CharBuf *wanted = S_get_cb("foo bar -100000 baz");
+    String *wanted = S_get_cb("foo bar -100000 baz");
     int32_t num = -100000;
-    CharBuf *got = S_get_cb("foo ");
-    CB_catf(got, "bar %i32 baz", num);
-    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "%%i32");
+    String *got = S_get_cb("foo ");
+    Str_catf(got, "bar %i32 baz", num);
+    TEST_TRUE(runner, Str_Equals(wanted, (Obj*)got), "%%i32");
     DECREF(wanted);
     DECREF(got);
 }
 
 static void
 test_vcatf_i64(TestBatchRunner *runner) {
-    CharBuf *wanted = S_get_cb("foo bar -5000000000 baz");
+    String *wanted = S_get_cb("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, CB_Equals(wanted, (Obj*)got), "%%i64");
+    String *got = S_get_cb("foo ");
+    Str_catf(got, "bar %i64 baz", num);
+    TEST_TRUE(runner, Str_Equals(wanted, (Obj*)got), "%%i64");
     DECREF(wanted);
     DECREF(got);
 }
 
 static void
 test_vcatf_u8(TestBatchRunner *runner) {
-    CharBuf *wanted = S_get_cb("foo bar 3 baz");
+    String *wanted = S_get_cb("foo bar 3 baz");
     uint8_t num = 3;
-    CharBuf *got = S_get_cb("foo ");
-    CB_catf(got, "bar %u8 baz", num);
-    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "%%u8");
+    String *got = S_get_cb("foo ");
+    Str_catf(got, "bar %u8 baz", num);
+    TEST_TRUE(runner, Str_Equals(wanted, (Obj*)got), "%%u8");
     DECREF(wanted);
     DECREF(got);
 }
 
 static void
 test_vcatf_u32(TestBatchRunner *runner) {
-    CharBuf *wanted = S_get_cb("foo bar 100000 baz");
+    String *wanted = S_get_cb("foo bar 100000 baz");
     uint32_t num = 100000;
-    CharBuf *got = S_get_cb("foo ");
-    CB_catf(got, "bar %u32 baz", num);
-    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "%%u32");
+    String *got = S_get_cb("foo ");
+    Str_catf(got, "bar %u32 baz", num);
+    TEST_TRUE(runner, Str_Equals(wanted, (Obj*)got), "%%u32");
     DECREF(wanted);
     DECREF(got);
 }
 
 static void
 test_vcatf_u64(TestBatchRunner *runner) {
-    CharBuf *wanted = S_get_cb("foo bar 5000000000 baz");
+    String *wanted = S_get_cb("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, CB_Equals(wanted, (Obj*)got), "%%u64");
+    String *got = S_get_cb("foo ");
+    Str_catf(got, "bar %u64 baz", num);
+    TEST_TRUE(runner, Str_Equals(wanted, (Obj*)got), "%%u64");
     DECREF(wanted);
     DECREF(got);
 }
 
 static void
 test_vcatf_f64(TestBatchRunner *runner) {
-    CharBuf *wanted;
+    String *wanted;
     char buf[64];
     float num = 1.3f;
-    CharBuf *got = S_get_cb("foo ");
+    String *got = S_get_cb("foo ");
     sprintf(buf, "foo bar %g baz", num);
-    wanted = CB_new_from_trusted_utf8(buf, strlen(buf));
-    CB_catf(got, "bar %f64 baz", num);
-    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "%%f64");
+    wanted = Str_new_from_trusted_utf8(buf, strlen(buf));
+    Str_catf(got, "bar %f64 baz", num);
+    TEST_TRUE(runner, Str_Equals(wanted, (Obj*)got), "%%f64");
     DECREF(wanted);
     DECREF(got);
 }
 
 static void
 test_vcatf_x32(TestBatchRunner *runner) {
-    CharBuf *wanted;
+    String *wanted;
     char buf[64];
     unsigned long num = INT32_MAX;
-    CharBuf *got = S_get_cb("foo ");
+    String *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 = CB_new_from_trusted_utf8(buf, strlen(buf));
-    CB_catf(got, "bar %x32 baz", (uint32_t)num);
-    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "%%x32");
+    wanted = Str_new_from_trusted_utf8(buf, strlen(buf));
+    Str_catf(got, "bar %x32 baz", (uint32_t)num);
+    TEST_TRUE(runner, Str_Equals(wanted, (Obj*)got), "%%x32");
     DECREF(wanted);
     DECREF(got);
 }

http://git-wip-us.apache.org/repos/asf/lucy/blob/2c3dbf15/clownfish/runtime/core/Clownfish/Test/TestErr.c
----------------------------------------------------------------------
diff --git a/clownfish/runtime/core/Clownfish/Test/TestErr.c b/clownfish/runtime/core/Clownfish/Test/TestErr.c
index bc4f434..5627d7d 100644
--- a/clownfish/runtime/core/Clownfish/Test/TestErr.c
+++ b/clownfish/runtime/core/Clownfish/Test/TestErr.c
@@ -19,7 +19,7 @@
 
 #include "Clownfish/Test/TestErr.h"
 
-#include "Clownfish/CharBuf.h"
+#include "Clownfish/String.h"
 #include "Clownfish/Err.h"
 #include "Clownfish/Test.h"
 #include "Clownfish/TestHarness/TestBatchRunner.h"
@@ -32,10 +32,10 @@ TestErr_new() {
 
 static void
 test_To_String(TestBatchRunner *runner) {
-    CharBuf *message = CB_newf("oops");
+    String *message = Str_newf("oops");
     Err *error = Err_new(message);
-    CharBuf *string = Err_To_String(error);
-    TEST_TRUE(runner, CB_Equals(message, (Obj*)string),
+    String *string = Err_To_String(error);
+    TEST_TRUE(runner, Str_Equals(message, (Obj*)string),
               "Stringifies as message");
     DECREF(string);
     DECREF(error);

http://git-wip-us.apache.org/repos/asf/lucy/blob/2c3dbf15/clownfish/runtime/core/Clownfish/Test/TestHash.c
----------------------------------------------------------------------
diff --git a/clownfish/runtime/core/Clownfish/Test/TestHash.c b/clownfish/runtime/core/Clownfish/Test/TestHash.c
index d90dba1..1e5bfcd 100644
--- a/clownfish/runtime/core/Clownfish/Test/TestHash.c
+++ b/clownfish/runtime/core/Clownfish/Test/TestHash.c
@@ -22,7 +22,7 @@
 
 #include "Clownfish/Test/TestHash.h"
 
-#include "Clownfish/CharBuf.h"
+#include "Clownfish/String.h"
 #include "Clownfish/Hash.h"
 #include "Clownfish/Num.h"
 #include "Clownfish/Test.h"
@@ -73,7 +73,7 @@ test_Store_and_Fetch(TestBatchRunner *runner) {
     StackString *foo          = SSTR_WRAP_STR("foo", 3);
 
     for (int32_t i = 0; i < 100; i++) {
-        CharBuf *cb = CB_newf("%i32", i);
+        String *cb = Str_newf("%i32", i);
         Hash_Store(hash, (Obj*)cb, (Obj*)cb);
         Hash_Store(dupe, (Obj*)cb, INCREF(cb));
         VA_Push(expected, INCREF(cb));
@@ -135,7 +135,7 @@ test_Keys_Values_Iter(TestBatchRunner *runner) {
     VArray   *values;
 
     for (uint32_t i = 0; i < 500; i++) {
-        CharBuf *cb = CB_newf("%u32", i);
+        String *cb = Str_newf("%u32", i);
         Hash_Store(hash, (Obj*)cb, (Obj*)cb);
         VA_Push(expected, INCREF(cb));
     }
@@ -190,7 +190,7 @@ test_stress(TestBatchRunner *runner) {
     VArray   *values;
 
     for (uint32_t i = 0; i < 1000; i++) {
-        CharBuf *cb = TestUtils_random_string(rand() % 1200);
+        String *cb = TestUtils_random_string(rand() % 1200);
         while (Hash_Fetch(hash, (Obj*)cb)) {
             DECREF(cb);
             cb = TestUtils_random_string(rand() % 1200);
@@ -203,7 +203,7 @@ test_stress(TestBatchRunner *runner) {
 
     // Overwrite for good measure.
     for (uint32_t i = 0; i < 1000; i++) {
-        CharBuf *cb = (CharBuf*)VA_Fetch(expected, i);
+        String *cb = (String*)VA_Fetch(expected, i);
         Hash_Store(hash, (Obj*)cb, INCREF(cb));
     }
 

http://git-wip-us.apache.org/repos/asf/lucy/blob/2c3dbf15/clownfish/runtime/core/Clownfish/Test/TestLockFreeRegistry.c
----------------------------------------------------------------------
diff --git a/clownfish/runtime/core/Clownfish/Test/TestLockFreeRegistry.c b/clownfish/runtime/core/Clownfish/Test/TestLockFreeRegistry.c
index 69f827b..515db9a 100644
--- a/clownfish/runtime/core/Clownfish/Test/TestLockFreeRegistry.c
+++ b/clownfish/runtime/core/Clownfish/Test/TestLockFreeRegistry.c
@@ -36,7 +36,7 @@ TestLFReg_new() {
 
 StupidHashCharBuf*
 StupidHashCharBuf_new(const char *text) {
-    return (StupidHashCharBuf*)CB_new_from_utf8(text, strlen(text));
+    return (StupidHashCharBuf*)Str_new_from_utf8(text, strlen(text));
 }
 
 int32_t

http://git-wip-us.apache.org/repos/asf/lucy/blob/2c3dbf15/clownfish/runtime/core/Clownfish/Test/TestLockFreeRegistry.cfh
----------------------------------------------------------------------
diff --git a/clownfish/runtime/core/Clownfish/Test/TestLockFreeRegistry.cfh b/clownfish/runtime/core/Clownfish/Test/TestLockFreeRegistry.cfh
index 039c573..80db8af 100644
--- a/clownfish/runtime/core/Clownfish/Test/TestLockFreeRegistry.cfh
+++ b/clownfish/runtime/core/Clownfish/Test/TestLockFreeRegistry.cfh
@@ -28,7 +28,7 @@ class Clownfish::Test::TestLockFreeRegistry cnick TestLFReg
 
 /** Private test-only class for stressing LockFreeRegistry.
  */
-class Clownfish::Test::StupidHashCharBuf inherits Clownfish::CharBuf {
+class Clownfish::Test::StupidHashCharBuf inherits Clownfish::String {
     inert incremented StupidHashCharBuf*
     new(const char *text);
 

http://git-wip-us.apache.org/repos/asf/lucy/blob/2c3dbf15/clownfish/runtime/core/Clownfish/Test/TestNum.c
----------------------------------------------------------------------
diff --git a/clownfish/runtime/core/Clownfish/Test/TestNum.c b/clownfish/runtime/core/Clownfish/Test/TestNum.c
index a1a95eb..0334aac 100644
--- a/clownfish/runtime/core/Clownfish/Test/TestNum.c
+++ b/clownfish/runtime/core/Clownfish/Test/TestNum.c
@@ -19,7 +19,7 @@
 
 #include "Clownfish/Test/TestNum.h"
 
-#include "Clownfish/CharBuf.h"
+#include "Clownfish/String.h"
 #include "Clownfish/Num.h"
 #include "Clownfish/Test.h"
 #include "Clownfish/TestHarness/TestBatchRunner.h"
@@ -37,24 +37,24 @@ test_To_String(TestBatchRunner *runner) {
     Float64   *f64 = Float64_new(1.33);
     Integer32 *i32 = Int32_new(INT32_MAX);
     Integer64 *i64 = Int64_new(INT64_MAX);
-    CharBuf *f32_string = Float32_To_String(f32);
-    CharBuf *f64_string = Float64_To_String(f64);
-    CharBuf *i32_string = Int32_To_String(i32);
-    CharBuf *i64_string = Int64_To_String(i64);
-    CharBuf *true_string  = Bool_To_String(CFISH_TRUE);
-    CharBuf *false_string = Bool_To_String(CFISH_FALSE);
-
-    TEST_TRUE(runner, CB_Starts_With_Str(f32_string, "1.3", 3),
+    String *f32_string = Float32_To_String(f32);
+    String *f64_string = Float64_To_String(f64);
+    String *i32_string = Int32_To_String(i32);
+    String *i64_string = Int64_To_String(i64);
+    String *true_string  = Bool_To_String(CFISH_TRUE);
+    String *false_string = Bool_To_String(CFISH_FALSE);
+
+    TEST_TRUE(runner, Str_Starts_With_Str(f32_string, "1.3", 3),
               "Float32_To_String");
-    TEST_TRUE(runner, CB_Starts_With_Str(f64_string, "1.3", 3),
+    TEST_TRUE(runner, Str_Starts_With_Str(f64_string, "1.3", 3),
               "Float64_To_String");
-    TEST_TRUE(runner, CB_Equals_Str(i32_string, "2147483647", 10),
+    TEST_TRUE(runner, Str_Equals_Str(i32_string, "2147483647", 10),
               "Int32_To_String");
-    TEST_TRUE(runner, CB_Equals_Str(i64_string, "9223372036854775807", 19),
+    TEST_TRUE(runner, Str_Equals_Str(i64_string, "9223372036854775807", 19),
               "Int64_To_String");
-    TEST_TRUE(runner, CB_Equals_Str(true_string, "true", 4),
+    TEST_TRUE(runner, Str_Equals_Str(true_string, "true", 4),
               "Bool_To_String [true]");
-    TEST_TRUE(runner, CB_Equals_Str(false_string, "false", 5),
+    TEST_TRUE(runner, Str_Equals_Str(false_string, "false", 5),
               "Bool_To_String [false]");
 
     DECREF(false_string);
@@ -200,7 +200,7 @@ test_Equals_and_Compare_To(TestBatchRunner *runner) {
                "CFISH_FALSE not Equals CFISH_TRUE ");
     TEST_FALSE(runner, Bool_Equals(CFISH_TRUE, (Obj*)CFISH_FALSE),
                "CFISH_TRUE not Equals CFISH_FALSE ");
-    TEST_FALSE(runner, Bool_Equals(CFISH_TRUE, (Obj*)CHARBUF),
+    TEST_FALSE(runner, Bool_Equals(CFISH_TRUE, (Obj*)STRING),
                "CFISH_TRUE not Equals random other object ");
 
     DECREF(i64_copy);

http://git-wip-us.apache.org/repos/asf/lucy/blob/2c3dbf15/clownfish/runtime/core/Clownfish/Test/TestObj.c
----------------------------------------------------------------------
diff --git a/clownfish/runtime/core/Clownfish/Test/TestObj.c b/clownfish/runtime/core/Clownfish/Test/TestObj.c
index b22add7..0d951be 100644
--- a/clownfish/runtime/core/Clownfish/Test/TestObj.c
+++ b/clownfish/runtime/core/Clownfish/Test/TestObj.c
@@ -24,7 +24,7 @@
 
 #include "Clownfish/Test/TestObj.h"
 
-#include "Clownfish/CharBuf.h"
+#include "Clownfish/String.h"
 #include "Clownfish/Err.h"
 #include "Clownfish/Test.h"
 #include "Clownfish/TestHarness/TestBatchRunner.h"
@@ -39,9 +39,9 @@ static Obj*
 S_new_testobj() {
     StackString *klass = SSTR_WRAP_STR("TestObj", 7);
     Obj *obj;
-    VTable *vtable = VTable_fetch_vtable((CharBuf*)klass);
+    VTable *vtable = VTable_fetch_vtable((String*)klass);
     if (!vtable) {
-        vtable = VTable_singleton((CharBuf*)klass, OBJ);
+        vtable = VTable_singleton((String*)klass, OBJ);
     }
     obj = VTable_Make_Obj(vtable);
     return Obj_init(obj);
@@ -66,7 +66,7 @@ test_refcounts(TestBatchRunner *runner) {
 static void
 test_To_String(TestBatchRunner *runner) {
     Obj *testobj = S_new_testobj();
-    CharBuf *string = Obj_To_String(testobj);
+    String *string = Obj_To_String(testobj);
     StackString *temp = SSTR_WRAP(string);
     while (SStr_Get_Size(temp)) {
         if (SStr_Starts_With_Str(temp, "TestObj", 7)) { break; }
@@ -103,14 +103,14 @@ test_Hash_Sum(TestBatchRunner *runner) {
 
 static void
 test_Is_A(TestBatchRunner *runner) {
-    CharBuf *charbuf   = CB_new(0);
-    VTable  *bb_vtable = CB_Get_VTable(charbuf);
-    CharBuf *klass     = CB_Get_Class_Name(charbuf);
-
-    TEST_TRUE(runner, CB_Is_A(charbuf, CHARBUF), "CharBuf Is_A CharBuf.");
-    TEST_TRUE(runner, CB_Is_A(charbuf, OBJ), "CharBuf Is_A Obj.");
-    TEST_TRUE(runner, bb_vtable == CHARBUF, "Get_VTable");
-    TEST_TRUE(runner, CB_Equals(VTable_Get_Name(CHARBUF), (Obj*)klass),
+    String *charbuf   = Str_new(0);
+    VTable  *bb_vtable = Str_Get_VTable(charbuf);
+    String *klass     = Str_Get_Class_Name(charbuf);
+
+    TEST_TRUE(runner, Str_Is_A(charbuf, STRING), "String Is_A String.");
+    TEST_TRUE(runner, Str_Is_A(charbuf, OBJ), "String Is_A Obj.");
+    TEST_TRUE(runner, bb_vtable == STRING, "Get_VTable");
+    TEST_TRUE(runner, Str_Equals(VTable_Get_Name(STRING), (Obj*)klass),
               "Get_Class_Name");
 
     DECREF(charbuf);
@@ -154,7 +154,7 @@ S_verify_abstract_error(TestBatchRunner *runner, Err_Attempt_t routine,
     Err *error = Err_trap(routine, context);
     TEST_TRUE(runner, error != NULL
               && Err_Is_A(error, ERR) 
-              && CB_Find_Str(Err_Get_Mess(error), "bstract", 7) != -1,
+              && Str_Find_Str(Err_Get_Mess(error), "bstract", 7) != -1,
               message);
     DECREF(error);
 }