comparison mde/content/AStringContent.d @ 164:c13bded1bed3

Some (somewhat pointless) memory optimisations for AStringContent.
author Diggory Hardy <diggory.hardy@gmail.com>
date Sat, 23 May 2009 17:23:21 +0200
parents 24d77c52243f
children bb2f1a76346d
comparison
equal deleted inserted replaced
163:24d77c52243f 164:c13bded1bed3
28 28
29 import tango.util.log.Log : Log, Logger; 29 import tango.util.log.Log : Log, Logger;
30 private Logger logger; 30 private Logger logger;
31 static this () { 31 static this () {
32 logger = Log.getLogger ("mde.content.AStringContent"); 32 logger = Log.getLogger ("mde.content.AStringContent");
33 }
34
35 private {
36 // Returns the length of memory to allocate, when len is the min required.
37 // Returns at least 66, since formatting from int/float requires this.
38 size_t allocLength (size_t len) {
39 return len < 33 ? 66 : len * 2;
40 }
33 } 41 }
34 42
35 /** Union of all content types here - for dynamic cast checking against several 43 /** Union of all content types here - for dynamic cast checking against several
36 * types. */ 44 * types. */
37 union UnionContent { 45 union UnionContent {
53 * pos <= sv.length). */ 61 * pos <= sv.length). */
54 abstract class AStringContent : Content 62 abstract class AStringContent : Content
55 { 63 {
56 protected this (char[] symbol) { 64 protected this (char[] symbol) {
57 super (symbol); 65 super (symbol);
66 svBuf.length = allocLength (0);
58 } 67 }
59 68
60 /// Get the text. 69 /// Get the text.
61 override char[] toString (uint i) { 70 override char[] toString (uint i) {
62 return i == 0 ? sv 71 return i == 0 ? sv
73 return endEdit; 82 return endEdit;
74 } 83 }
75 return false; 84 return false;
76 } 85 }
77 86
78 /** Acts on a keystroke and returns the new value. 87 /** Acts on a keystroke to edit the value as a string.
88 *
89 * After this has been used to edit the string, endEdit should be called
90 * to convert back to the native value type.
79 * 91 *
80 * Supports one-line editing: left/right, home/end, backspace/delete. */ 92 * Supports one-line editing: left/right, home/end, backspace/delete. */
81 char[] keyStroke (ushort sym, char[] i) { 93 char[] keyStroke (ushort sym, char[] i) {
94 // This routine relies on the folowing: svBuf[0..sv.length] == sv
95 if (sv.ptr !is svBuf.ptr) {
96 // sv may be within svBuf but not starting at its beginning
97 //NOTE: to further optimise, try to avoid a realloc on the heap
98 svBuf = new char[allocLength (sv.length)];
99 svBuf[0..sv.length] = sv;
100 }
101 //NOTE (optimise): dup is used several times for temporary storage
102 //perhaps could be optimised by a dup func using thread-local buffer?
82 debug assert (i.length, "StringContent.keyStroke: no value (??)"); // impossible? 103 debug assert (i.length, "StringContent.keyStroke: no value (??)"); // impossible?
83 char k = *i; 104 char k = *i;
84 if (k >= 0x20) { 105 if (k >= 0x20) {
85 if (k == 0x7f) { // delete 106 if (k == 0x7f) { // delete
86 size_t p = pos; 107 size_t p = pos;
87 if (p < sv.length) ++p; 108 if (p < sv.length) ++p;
88 while (p < sv.length && (sv[p] & 0x80) && !(sv[p] & 0x40)) 109 while (p < sv.length && (sv[p] & 0x80) && !(sv[p] & 0x40))
89 ++p; 110 ++p;
90 sv = sv[0..pos] ~ sv[p..$]; 111 size_t l = sv.length - (p-pos);
112 sv[pos..l] = sv[p..$].dup;
113 sv = sv[0..l];
91 } else { // insert character 114 } else { // insert character
92 char[] tail = sv[pos..$]; 115 char[] tail = sv[pos..$];
93 //NOTE: reallocating each keypress isn't optimal 116 size_t l = sv.length + i.length;
94 sv.length = sv.length + i.length; 117 if (svBuf.length < l)
118 svBuf.length = allocLength (l);
95 size_t npos = pos+i.length; 119 size_t npos = pos+i.length;
96 if (tail) sv[npos..$] = tail.dup; // cannot assign with overlapping ranges 120 if (tail) svBuf[npos..l] = tail.dup;
97 sv[pos..npos] = i; 121 svBuf[pos..npos] = i;
122 sv = svBuf[0..l];
98 pos = npos; 123 pos = npos;
99 } 124 }
100 } else { // use sym; many keys output 0 125 } else { // use sym; many keys output 0
101 if (sym >= SDLK_RSHIFT && (sym <= SDLK_COMPOSE || sym == SDLK_MENU)) { 126 if (sym >= SDLK_RSHIFT && (sym <= SDLK_COMPOSE || sym == SDLK_MENU)) {
102 } // all modifier keys and contect menu key; should be ignored 127 } // all modifier keys and contect menu key; should be ignored
103 else if (sym == SDLK_BACKSPACE) { // backspace; k == 0x8 128 else if (sym == SDLK_BACKSPACE) { // backspace; k == 0x8
104 char[] tail = sv[pos..$]; 129 char[] tail = sv[pos..$];
130 size_t l = pos;
105 if (pos) --pos; 131 if (pos) --pos;
106 while (pos && (sv[pos] & 0x80) && !(sv[pos] & 0x40)) 132 while (pos && (sv[pos] & 0x80) && !(sv[pos] & 0x40))
107 --pos; 133 --pos;
108 sv = sv[0..pos] ~ tail; 134 l = sv.length - (l - pos);
135 sv[pos..l] = tail.dup;
136 sv = sv[0..l];
109 } else if (sym == SDLK_LEFT) { 137 } else if (sym == SDLK_LEFT) {
110 if (pos) --pos; 138 if (pos) --pos;
111 while (pos && (sv[pos] & 0x80) && !(sv[pos] & 0x40)) 139 while (pos && (sv[pos] & 0x80) && !(sv[pos] & 0x40))
112 --pos; 140 --pos;
113 } else if (sym == SDLK_RIGHT) { 141 } else if (sym == SDLK_RIGHT) {
153 * Should never throw; should reset sv at least when returning false. */ 181 * Should never throw; should reset sv at least when returning false. */
154 bool endEdit (); 182 bool endEdit ();
155 183
156 protected: 184 protected:
157 /* String version of value (for toString(0) and editing). 185 /* String version of value (for toString(0) and editing).
158 * WARNING: This must point to mutable memory! 186 * WARNING: This must point to mutable memory (for endEdit), and
159 * (Actually this isn't usually required now, but after optimising will be.) 187 * when keyStroke can be called we must have svBuf[0..sv.length] == sv. */
160 * TODO: provide a buffer, for use when editing sv. */
161 char[] sv; 188 char[] sv;
189 char[] svBuf; // buffer to reduce reallocs
162 size_t pos; // editing position; used by keyStroke 190 size_t pos; // editing position; used by keyStroke
163 } 191 }
164 192
165 class BoolContent : AStringContent 193 class BoolContent : AStringContent
166 { 194 {
167 /** Create a content with _symbol name symbol. */ 195 /** Create a content with _symbol name symbol. */
168 this (char[] symbol) { 196 this (char[] symbol) {
169 auto valp = symbol in changed.boolData; 197 auto valp = symbol in changed.boolData;
170 if (valp) 198 if (valp)
171 v = *valp; 199 v = *valp;
172 sv = (v ? "true" : "false").dup; 200 sv = v ? "true" : "false";
173 super (symbol); 201 super (symbol);
174 } 202 }
175 203
176 // Assign without adding change to save changeset 204 // Assign without adding change to save changeset
177 void assignNoCng (bool val) { 205 void assignNoCng (bool val) {
178 v = val; 206 v = val;
179 sv = (v ? "true" : "false").dup; 207 sv = v ? "true" : "false";
180 if (pos > sv.length) pos = sv.length; 208 if (pos > sv.length) pos = sv.length;
181 endEvent; 209 endEvent;
182 } 210 }
183 void opAssign (bool val) { 211 void opAssign (bool val) {
184 assignNoCng (val); 212 assignNoCng (val);
198 v = 1; 226 v = 1;
199 else // throws if can't convert to int: 227 else // throws if can't convert to int:
200 v = (Int.toLong (sv) != 0); 228 v = (Int.toLong (sv) != 0);
201 } catch (Exception e) { 229 } catch (Exception e) {
202 logger.error (e.msg); 230 logger.error (e.msg);
203 sv = (v ? "true" : "false").dup; 231 sv = v ? "true" : "false";
204 return false; 232 return false;
205 } 233 }
206 sv = (v ? "true" : "false").dup; 234 sv = v ? "true" : "false";
207 endEvent; 235 endEvent;
208 endCng; 236 endCng;
209 return true; 237 return true;
210 } 238 }
211 239
259 /** Integer content. */ 287 /** Integer content. */
260 class IntContent : AStringContent 288 class IntContent : AStringContent
261 { 289 {
262 /** Create a content with _symbol name symbol. */ 290 /** Create a content with _symbol name symbol. */
263 this (char[] symbol) { 291 this (char[] symbol) {
264 auto valp = symbol in changed.intData; 292 super (symbol);
293 auto valp = symbol in changed.intData;
265 if (valp) 294 if (valp)
266 v = *valp; 295 v = *valp;
267 sv = Int.toString (v); 296 sv = Int.format (svBuf, v);
268 super (symbol);
269 } 297 }
270 298
271 override bool set (IContent c) { 299 override bool set (IContent c) {
272 UnionContent uc; 300 UnionContent uc;
273 uc.bc = cast (BoolContent) c; 301 uc.bc = cast (BoolContent) c;
288 return super.set (c); 316 return super.set (c);
289 } 317 }
290 318
291 void assignNoCng (int val) { 319 void assignNoCng (int val) {
292 v = val; 320 v = val;
293 sv = Int.toString (v); 321 sv = Int.format (svBuf, v);
294 if (pos > sv.length) pos = sv.length; 322 if (pos > sv.length) pos = sv.length;
295 endEvent; 323 endEvent;
296 } 324 }
297 void opAssign (int val) { 325 void opAssign (int val) {
298 assignNoCng (val); 326 assignNoCng (val);
303 } 331 }
304 alias opCall opCast; 332 alias opCall opCast;
305 333
306 override bool endEdit () { 334 override bool endEdit () {
307 try { 335 try {
308 v = Int.toInt (sv); 336 // use toFloat to allow decimal points and exponents
337 v = Math.rndint (Float.toFloat (sv));
309 } catch (Exception e) { 338 } catch (Exception e) {
310 logger.error (e.msg); 339 logger.error (e.msg);
311 sv = Int.toString (v); 340 sv = Int.format (svBuf, v);
312 return false; 341 return false;
313 } 342 }
314 sv = Int.toString (v); 343 sv = Int.format (svBuf, v);
315 endEvent; 344 endEvent;
316 endCng; 345 endCng;
317 return true; 346 return true;
318 } 347 }
319 348
328 /** Floating-point content. */ 357 /** Floating-point content. */
329 class DoubleContent : AStringContent 358 class DoubleContent : AStringContent
330 { 359 {
331 /** Create a content with _symbol name symbol. */ 360 /** Create a content with _symbol name symbol. */
332 this (char[] symbol) { 361 this (char[] symbol) {
333 auto valp = symbol in changed.doubleData; 362 super (symbol);
363 auto valp = symbol in changed.doubleData;
334 if (valp) 364 if (valp)
335 v = *valp; 365 v = *valp;
336 sv = Float.toString (v, 8, 4); 366 sv = Float.format (svBuf, v, 8, 4);
337 super (symbol);
338 } 367 }
339 368
340 override bool set (IContent c) { 369 override bool set (IContent c) {
341 UnionContent uc; 370 UnionContent uc;
342 uc.bc = cast (BoolContent) c; 371 uc.bc = cast (BoolContent) c;
357 return super.set (c); 386 return super.set (c);
358 } 387 }
359 388
360 void assignNoCng (double val) { 389 void assignNoCng (double val) {
361 v = val; 390 v = val;
362 sv = Float.toString (v, 8, 4); 391 sv = Float.format (svBuf, v, 8, 4);
363 if (pos > sv.length) pos = sv.length; 392 if (pos > sv.length) pos = sv.length;
364 endEvent; 393 endEvent;
365 } 394 }
366 void opAssign (double val) { 395 void opAssign (double val) {
367 assignNoCng (val); 396 assignNoCng (val);
375 override bool endEdit () { 404 override bool endEdit () {
376 try { 405 try {
377 v = Float.toFloat (sv); 406 v = Float.toFloat (sv);
378 } catch (Exception e) { 407 } catch (Exception e) {
379 logger.error (e.msg); 408 logger.error (e.msg);
380 sv = Float.toString (v, 8, 4); 409 sv = Float.format (svBuf, v, 8, 4);
381 return false; 410 return false;
382 } 411 }
383 sv = Float.toString (v, 8, 4); 412 sv = Float.format (svBuf, v, 8, 4);
384 endEvent; 413 endEvent;
385 endCng; 414 endCng;
386 return true; 415 return true;
387 } 416 }
388 417
410 char[] symPeriod = symbol~'.'; 439 char[] symPeriod = symbol~'.';
411 foreach (i, ref e; enums) { 440 foreach (i, ref e; enums) {
412 e = new EnumValueContent (this, i, symPeriod~enumSymbols[i]); 441 e = new EnumValueContent (this, i, symPeriod~enumSymbols[i]);
413 } 442 }
414 enums[v].assignFromParent (true); 443 enums[v].assignFromParent (true);
415 sv = enums[v].name_.dup; 444 sv = enums[v].name_;
416 // Re-set the value if a saved value is found: 445 // Re-set the value if a saved value is found:
417 auto valp = symbol in changed.enumValData; 446 auto valp = symbol in changed.enumValData;
418 if (valp) 447 if (valp)
419 assignNoCng = *valp; 448 assignNoCng = *valp;
420 } 449 }
444 return; 473 return;
445 } 474 }
446 enums[v] .assignFromParent (false); 475 enums[v] .assignFromParent (false);
447 enums[val].assignFromParent (true); 476 enums[val].assignFromParent (true);
448 v = val; 477 v = val;
449 sv = enums[v].name_.dup; 478 sv = enums[v].name_;
450 if (pos > sv.length) pos = sv.length; 479 if (pos > sv.length) pos = sv.length;
451 endEvent; 480 endEvent;
452 } 481 }
453 482
454 override bool endEdit () { 483 override bool endEdit () {
456 if (sv == e.name_) { 485 if (sv == e.name_) {
457 assignNoCng (i); 486 assignNoCng (i);
458 goto break1; 487 goto break1;
459 } 488 }
460 489
461 sv = enums[v].name_.dup; // sv was edited; revert 490 sv = enums[v].name_; // sv was edited; revert
462 logger.error ("EnumContent "~name_~" assigned invalid value; keeping value: "~sv); 491 logger.error ("EnumContent "~name_~" assigned invalid value; keeping value: "~sv);
463 if (pos > sv.length) pos = sv.length; 492 if (pos > sv.length) pos = sv.length;
464 return false; 493 return false;
465 494
466 break1: 495 break1: