changeset 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
files mde/content/AStringContent.d
diffstat 1 files changed, 59 insertions(+), 30 deletions(-) [+]
line wrap: on
line diff
--- a/mde/content/AStringContent.d	Sat May 23 15:47:32 2009 +0200
+++ b/mde/content/AStringContent.d	Sat May 23 17:23:21 2009 +0200
@@ -32,6 +32,14 @@
     logger = Log.getLogger ("mde.content.AStringContent");
 }
 
+private {
+    // Returns the length of memory to allocate, when len is the min required.
+    // Returns at least 66, since formatting from int/float requires this.
+    size_t allocLength (size_t len) {
+	return len < 33 ? 66 : len * 2;
+    }
+}
+
 /** Union of all content types here - for dynamic cast checking against several
  * types. */
 union UnionContent {
@@ -55,6 +63,7 @@
 {
     protected this (char[] symbol) {
 	super (symbol);
+	svBuf.length = allocLength (0);
     }
     
     /// Get the text.
@@ -75,10 +84,22 @@
 	return false;
     }
     
-    /** Acts on a keystroke and returns the new value.
+    /** Acts on a keystroke to edit the value as a string.
+     *
+     * After this has been used to edit the string, endEdit should be called
+     * to convert back to the native value type.
      *
      * Supports one-line editing: left/right, home/end, backspace/delete. */
     char[] keyStroke (ushort sym, char[] i) {
+	// This routine relies on the folowing: svBuf[0..sv.length] == sv
+	if (sv.ptr !is svBuf.ptr) {
+	    // sv may be within svBuf but not starting at its beginning
+	    //NOTE: to further optimise, try to avoid a realloc on the heap
+	    svBuf = new char[allocLength (sv.length)];
+	    svBuf[0..sv.length] = sv;
+	}
+	//NOTE (optimise): dup is used several times for temporary storage
+	//perhaps could be optimised by a dup func using thread-local buffer?
 	debug assert (i.length, "StringContent.keyStroke: no value (??)");	// impossible?
 	char k = *i;
 	if (k >= 0x20) {
@@ -87,14 +108,18 @@
 		if (p < sv.length) ++p;
 		while (p < sv.length && (sv[p] & 0x80) && !(sv[p] & 0x40))
 		    ++p;
-		sv = sv[0..pos] ~ sv[p..$];
+		size_t l = sv.length - (p-pos);
+		sv[pos..l] = sv[p..$].dup;
+		sv = sv[0..l];
 	    } else {			// insert character
 		char[] tail = sv[pos..$];
-		//NOTE: reallocating each keypress isn't optimal
-		sv.length = sv.length + i.length;
+		size_t l = sv.length + i.length;
+		if (svBuf.length < l)
+		    svBuf.length = allocLength (l);
 		size_t npos = pos+i.length;
-		if (tail) sv[npos..$] = tail.dup;	// cannot assign with overlapping ranges
-		sv[pos..npos] = i;
+		if (tail) svBuf[npos..l] = tail.dup;
+		svBuf[pos..npos] = i;
+		sv = svBuf[0..l];
 		pos = npos;
 	    }
 	} else {			// use sym; many keys output 0
@@ -102,10 +127,13 @@
             }	// all modifier keys and contect menu key; should be ignored
 	    else if (sym == SDLK_BACKSPACE) {	// backspace; k == 0x8
 		char[] tail = sv[pos..$];
+		size_t l = pos;
 		if (pos) --pos;
 		while (pos && (sv[pos] & 0x80) && !(sv[pos] & 0x40))
 		    --pos;
-		sv = sv[0..pos] ~ tail;
+		l = sv.length - (l - pos);
+		sv[pos..l] = tail.dup;
+		sv = sv[0..l];
 	    } else if (sym == SDLK_LEFT) {
 		if (pos) --pos;
 		while (pos && (sv[pos] & 0x80) && !(sv[pos] & 0x40))
@@ -155,10 +183,10 @@
     
 protected:
     /* String version of value (for toString(0) and editing).
-     * WARNING: This must point to mutable memory!
-     * (Actually this isn't usually required now, but after optimising will be.)
-     * TODO: provide a buffer, for use when editing sv. */
+     * WARNING: This must point to mutable memory (for endEdit), and
+     * when keyStroke can be called we must have svBuf[0..sv.length] == sv. */
     char[] sv;
+    char[] svBuf;	// buffer to reduce reallocs
     size_t pos;		// editing position; used by keyStroke
 }
 
@@ -169,14 +197,14 @@
         auto valp = symbol in changed.boolData;
         if (valp)
             v = *valp;
-        sv = (v ? "true" : "false").dup;
+        sv = v ? "true" : "false";
 	super (symbol);
     }
     
     // Assign without adding change to save changeset
     void assignNoCng (bool val) {
 	v = val;
-	sv = (v ? "true" : "false").dup;
+	sv = v ? "true" : "false";
 	if (pos > sv.length) pos = sv.length;
         endEvent;
     }
@@ -200,10 +228,10 @@
 		v = (Int.toLong (sv) != 0);
 	} catch (Exception e) {
 	    logger.error (e.msg);
-	    sv = (v ? "true" : "false").dup;
+	    sv = v ? "true" : "false";
 	    return false;
 	}
-	sv = (v ? "true" : "false").dup;
+	sv = v ? "true" : "false";
         endEvent;
         endCng;
 	return true;
@@ -261,11 +289,11 @@
 {
     /** Create a content with _symbol name symbol. */
     this (char[] symbol) {
-        auto valp = symbol in changed.intData;
+	super (symbol);
+	auto valp = symbol in changed.intData;
         if (valp)
             v = *valp;
-        sv = Int.toString (v);
-	super (symbol);
+        sv = Int.format (svBuf, v);
     }
     
     override bool set (IContent c) {
@@ -290,7 +318,7 @@
     
     void assignNoCng (int val) {
 	v = val;
-	sv = Int.toString (v);
+	sv = Int.format (svBuf, v);
 	if (pos > sv.length) pos = sv.length;
         endEvent;
     }
@@ -305,13 +333,14 @@
     
     override bool endEdit () {
 	try {
-            v = Int.toInt (sv);
+	    // use toFloat to allow decimal points and exponents
+            v = Math.rndint (Float.toFloat (sv));
         } catch (Exception e) {
             logger.error (e.msg);
-	    sv = Int.toString (v);
+	    sv = Int.format (svBuf, v);
 	    return false;
 	}
-        sv = Int.toString (v);
+        sv = Int.format (svBuf, v);
         endEvent;
         endCng;
 	return true;
@@ -330,11 +359,11 @@
 {
     /** Create a content with _symbol name symbol. */
     this (char[] symbol) {
-        auto valp = symbol in changed.doubleData;
+	super (symbol);
+	auto valp = symbol in changed.doubleData;
         if (valp)
             v = *valp;
-        sv = Float.toString (v, 8, 4);
-	super (symbol);
+        sv = Float.format (svBuf, v, 8, 4);
     }
     
     override bool set (IContent c) {
@@ -359,7 +388,7 @@
     
     void assignNoCng (double val) {
 	v = val;
-	sv = Float.toString (v, 8, 4);
+	sv = Float.format (svBuf, v, 8, 4);
 	if (pos > sv.length) pos = sv.length;
         endEvent;
     }
@@ -377,10 +406,10 @@
             v = Float.toFloat (sv);
         } catch (Exception e) {
             logger.error (e.msg);
-	    sv = Float.toString (v, 8, 4);
+	    sv = Float.format (svBuf, v, 8, 4);
 	    return false;
 	}
-        sv = Float.toString (v, 8, 4);
+        sv = Float.format (svBuf, v, 8, 4);
         endEvent;
         endCng;
 	return true;
@@ -412,7 +441,7 @@
             e = new EnumValueContent (this, i, symPeriod~enumSymbols[i]);
         }
         enums[v].assignFromParent (true);
-        sv = enums[v].name_.dup;
+	sv = enums[v].name_;
         // Re-set the value if a saved value is found:
         auto valp = symbol in changed.enumValData;
         if (valp)
@@ -446,7 +475,7 @@
         enums[v]  .assignFromParent (false);
         enums[val].assignFromParent (true);
 	v = val;
-        sv = enums[v].name_.dup;
+        sv = enums[v].name_;
         if (pos > sv.length) pos = sv.length;
         endEvent;
     }
@@ -458,7 +487,7 @@
 		goto break1;
 	    }
 	
-        sv = enums[v].name_.dup;	// sv was edited; revert
+        sv = enums[v].name_;	// sv was edited; revert
         logger.error ("EnumContent "~name_~" assigned invalid value; keeping value: "~sv);
 	if (pos > sv.length) pos = sv.length;
 	return false;