1 #include "mystring.h"
2 #include "trace.h"
3 #include <ctype.h>
4 #include <string.h>
5 
6 mystringrep nil = { 0, 1, 1, "" };
7 
8 static const unsigned replength = sizeof(unsigned)*3;
9 
10 static const unsigned sizestep = sizeof(unsigned);
11 static const unsigned slackdiv = 4;
12 static const unsigned slackmax = 16;
13 
14 #ifdef MYSTRINGREP_STATS
15 
16 #include "fdbuf.h"
17 
18 struct _rep_stats
19 {
20   unsigned allocs;
21   unsigned alloc_size;
22   unsigned alloc_len;
23 
24   unsigned appends;
25   unsigned appends_dup;
26 
_rep_stats_rep_stats27   _rep_stats()
28     : allocs(0)
29     {
30     }
31 
stat_rep_stats32   void stat(const char* name, unsigned value)
33     {
34       ferr << "mystringrep: " << name << ": " << value << '\n';
35     }
pcnt_rep_stats36   void pcnt(const char* name, unsigned denom, unsigned divis)
37     {
38       ferr << "mystringrep: " << name << ": "
39 	   << denom << '/' << divis << '=';
40       if(divis) ferr << denom * 100 / divis << '%';
41       else ferr << "N/A";
42       ferr << '\n';
43     }
44 
~_rep_stats_rep_stats45   ~_rep_stats()
46     {
47       stat("     size step", sizestep);
48       stat(" slack divisor", slackdiv);
49       stat(" slack maximum", slackmax);
50       stat("        allocs", allocs);
51       stat("  alloc length", alloc_len);
52       stat("    alloc size", alloc_size);
53       pcnt("   alloc slack", alloc_size-alloc_len, alloc_len);
54       stat("alloc overhead", allocs*replength);
55       pcnt("  appends->dup", appends_dup, appends);
56     }
57 };
58 
59 static _rep_stats stats;
60 
61 #define ACCOUNT(NAME,VALUE) stats. NAME += VALUE
62 
63 #else // MYSTRINGREP_STATS
64 
65 #define ACCOUNT(NAME,VALUE)
66 
67 #endif // MYSTRINGREP_STATS
68 
69 ///////////////////////////////////////////////////////////////////////////////
70 // class mystringrep
71 ///////////////////////////////////////////////////////////////////////////////
alloc(unsigned length)72 mystringrep* mystringrep::alloc(unsigned length)
73 {
74   ACCOUNT(allocs, 1);
75   trace_static("length=" << length);
76   if(length == 0)
77     return &nil;
78 
79   ACCOUNT(alloc_len, length);
80   unsigned slack = length / slackdiv;
81   if(slack > slackmax)
82     slack = slackmax;
83   unsigned size = length+1 + sizestep-1 + slack;
84   size = size - size % sizestep;
85   ACCOUNT(alloc_size, size);
86 
87   mystringrep* ptr = (mystringrep*)new char[size+replength];
88   ptr->length = length;
89   ptr->references = 0;
90   ptr->size = size;
91   return ptr;
92 }
93 
dup(const char * str,unsigned length)94 mystringrep* mystringrep::dup(const char* str, unsigned length)
95 {
96   trace_static("str=" << (void*)str << " length=" << length);
97   if(length == 0)
98     return &nil;
99   mystringrep* ptr = alloc(length);
100   memcpy(ptr->buf, str, length);
101   ptr->buf[length] = 0;
102   return ptr;
103 }
104 
dup(const char * str1,unsigned length1,const char * str2,unsigned length2)105 mystringrep* mystringrep::dup(const char* str1, unsigned length1,
106 			      const char* str2, unsigned length2)
107 {
108   trace_static("");
109   if(length1+length2 == 0)
110     return &nil;
111   mystringrep* ptr = alloc(length1+length2);
112   memcpy(ptr->buf, str1, length1);
113   memcpy(ptr->buf+length1, str2, length2);
114   ptr->buf[length1+length2] = 0;
115   return ptr;
116 }
117 
append(const char * str,unsigned len)118 mystringrep* mystringrep::append(const char* str, unsigned len)
119 {
120   ACCOUNT(appends, 1);
121   unsigned newlen = length + len;
122   // If there are more than one references, always make a duplicate
123   // Also, if this does not have enough space to add the new string, dup it
124   if(references > 1 || newlen >= size) {
125     ACCOUNT(appends_dup, 1);
126     mystringrep* tmp = dup(buf, length, str, len);
127     tmp->attach();
128     detach();
129     return tmp;
130   }
131   // Otherwise, just add the new string to the end of this
132   else {
133     memcpy(buf+length, str, len);
134     buf[newlen] = 0;
135     length = newlen;
136     return this;
137   }
138 }
139 
140 #ifdef MYSTRING_TRACE
attach()141 void mystringrep::attach()
142 {
143   trace("references=" << references);
144   ++references;
145 }
146 #endif
147 
detach()148 void mystringrep::detach()
149 {
150   trace("references=" << references);
151 
152   --references;
153   if(!references) {
154     trace("deleting this");
155     delete this;
156   }
157 }
158