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