Mercurial > projects > mde
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: |