lib/mystring/rep.cc
changeset 0 6f7a81934006
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/mystring/rep.cc	Wed Jan 16 22:39:43 2008 +0100
@@ -0,0 +1,157 @@
+#include "mystring.h"
+#include "trace.h"
+#include <ctype.h>
+#include <string.h>
+
+mystringrep nil = { 0, 1, 1, "" };
+
+static const unsigned replength = sizeof(unsigned)*3;
+
+static const unsigned sizestep = sizeof(unsigned);
+static const unsigned slackdiv = 4;
+static const unsigned slackmax = 16;
+
+#ifdef MYSTRINGREP_STATS
+
+#include "fdbuf.h"
+
+struct _rep_stats
+{
+  unsigned allocs;
+  unsigned alloc_size;
+  unsigned alloc_len;
+  
+  unsigned appends;
+  unsigned appends_dup;
+
+  _rep_stats()
+    : allocs(0)
+    {
+    }
+  
+  void stat(const char* name, unsigned value)
+    {
+      ferr << "mystringrep: " << name << ": " << value << '\n';
+    }
+  void pcnt(const char* name, unsigned denom, unsigned divis)
+    {
+      ferr << "mystringrep: " << name << ": "
+	   << denom << '/' << divis << '=';
+      if(divis) ferr << denom * 100 / divis << '%';
+      else ferr << "N/A";
+      ferr << '\n';
+    }
+  
+  ~_rep_stats()
+    {
+      stat("     size step", sizestep);
+      stat(" slack divisor", slackdiv);
+      stat(" slack maximum", slackmax);
+      stat("        allocs", allocs);
+      stat("  alloc length", alloc_len);
+      stat("    alloc size", alloc_size);
+      pcnt("   alloc slack", alloc_size-alloc_len, alloc_len);
+      stat("alloc overhead", allocs*replength);
+      pcnt("  appends->dup", appends_dup, appends);
+    }
+};
+
+static _rep_stats stats;
+
+#define ACCOUNT(NAME,VALUE) stats. NAME += VALUE
+
+#else // MYSTRINGREP_STATS
+
+#define ACCOUNT(NAME,VALUE)
+
+#endif // MYSTRINGREP_STATS
+
+///////////////////////////////////////////////////////////////////////////////
+// class mystringrep
+///////////////////////////////////////////////////////////////////////////////
+mystringrep* mystringrep::alloc(unsigned length)
+{
+  ACCOUNT(allocs, 1);
+  trace_static("length=" << length);
+  if(length == 0)
+    return &nil;
+
+  ACCOUNT(alloc_len, length);
+  unsigned slack = length / slackdiv;
+  if(slack > slackmax)
+    slack = slackmax;
+  unsigned size = length+1 + sizestep-1 + slack;
+  size = size - size % sizestep;
+  ACCOUNT(alloc_size, size);
+
+  mystringrep* ptr = (mystringrep*)new char[size+replength];
+  ptr->length = length;
+  ptr->references = 0;
+  ptr->size = size;
+  return ptr;
+}
+
+mystringrep* mystringrep::dup(const char* str, unsigned length)
+{
+  trace_static("str=" << (void*)str << " length=" << length);
+  if(length == 0)
+    return &nil;
+  mystringrep* ptr = alloc(length);
+  memcpy(ptr->buf, str, length);
+  ptr->buf[length] = 0;
+  return ptr;
+}
+
+mystringrep* mystringrep::dup(const char* str1, unsigned length1,
+			      const char* str2, unsigned length2)
+{
+  trace_static("");
+  if(length1+length2 == 0)
+    return &nil;
+  mystringrep* ptr = alloc(length1+length2);
+  memcpy(ptr->buf, str1, length1);
+  memcpy(ptr->buf+length1, str2, length2);
+  ptr->buf[length1+length2] = 0;
+  return ptr;
+}
+
+mystringrep* mystringrep::append(const char* str, unsigned len)
+{
+  ACCOUNT(appends, 1);
+  unsigned newlen = length + len;
+  // If there are more than one references, always make a duplicate
+  // Also, if this does not have enough space to add the new string, dup it
+  if(references > 1 || newlen >= size) {
+    ACCOUNT(appends_dup, 1);
+    mystringrep* tmp = dup(buf, length, str, len);
+    tmp->attach();
+    detach();
+    return tmp;
+  }
+  // Otherwise, just add the new string to the end of this
+  else {
+    memcpy(buf+length, str, len);
+    buf[newlen] = 0;
+    length = newlen;
+    return this;
+  }
+}
+
+#ifdef MYSTRING_TRACE    
+void mystringrep::attach()
+{
+  trace("references=" << references);
+  ++references;
+}
+#endif
+
+void mystringrep::detach()
+{
+  trace("references=" << references);
+  
+  --references;
+  if(!references) {
+    trace("deleting this");
+    delete this;
+  }
+}