|
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 |
|
27 _rep_stats() |
|
28 : allocs(0) |
|
29 { |
|
30 } |
|
31 |
|
32 void stat(const char* name, unsigned value) |
|
33 { |
|
34 ferr << "mystringrep: " << name << ": " << value << '\n'; |
|
35 } |
|
36 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 |
|
45 ~_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 /////////////////////////////////////////////////////////////////////////////// |
|
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 |
|
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 |
|
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 |
|
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 |
|
141 void mystringrep::attach() |
|
142 { |
|
143 trace("references=" << references); |
|
144 ++references; |
|
145 } |
|
146 #endif |
|
147 |
|
148 void mystringrep::detach() |
|
149 { |
|
150 trace("references=" << references); |
|
151 |
|
152 --references; |
|
153 if(!references) { |
|
154 trace("deleting this"); |
|
155 delete this; |
|
156 } |
|
157 } |