When I first wrote about master strings I proposed a function that would help to trim them down and potentially save a lot of memory. However, that method still resulted in a string with a master string one longer than it. Ideally, we’d have no master string at all. Since then, three astute readers chimed in with alternate solutions to the problem. Today I put try all three out to see which method does the best job of cleaning master strings.

As a refresher, here’s the version I came up with in the previous article. For clarity, I’ve renamed it to cleanMasterStringUnderscore since it appends an underscore to the String and uses that as a master string instead.

public static function cleanMasterStringUnderscore(str:String): String
{
	return ("_"+str).substr(1);
}

Next is the suggestion to use two String.substr() calls. The first call gets the first character and the second gets the rest. These two parts are then concatenated.

public static function cleanMasterStringDoubleSubstr(str:String): String
{
	return str.substr(0,1)+str.substr(1);
}

A third solution is to put the String in an Object.

public static function cleanMasterStringObject(str:String): String
{
	var map:Object = {};
	map[str] = str;
	return str;
}

Lastly, the Alternativa3D solution is to write the String to a ByteArray and then read it back.

public static function cleanMasterStringByteArray(str:String): String
{
	var bytes:ByteArray = new ByteArray();
	bytes.writeUTFBytes(str);
	bytes.position = 0;
	return bytes.readUTFBytes(bytes.length);
}

The following test app tries out all four methods and prints the results on each frame, just in case the garbage collector ever kicks in.

package
{
	import flash.system.Capabilities;
	import flash.events.Event;
	import flash.sampler.getMasterString;
	import flash.display.*;
	import flash.text.*;
	import flash.utils.*;
 
	public class MasterStringTest extends Sprite
	{
		private var logger:TextField = new TextField();
 
		private var str:String;
		private var strCleanUnderscore:String;
		private var strCleanDoubleSubstr:String;
		private var strCleanObject:String;
		private var strCleanByteArray:String;
 
		public function MasterStringTest()
		{
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;
 
			logger.autoSize = TextFieldAutoSize.LEFT;
			addChild(logger);
 
			if (!Capabilities.isDebugger)
			{
				logger.text = "Debug version of Flash Player required to run this test";
				return;
			}
 
			// Get a String with a big master String
			var xml:XML = new XML(
				'<people><person first="John"/></people>'
			);
			str = xml.person[0].@first;
 
			// Clean with the underscore-prepending method
			strCleanUnderscore = cleanMasterStringUnderscore(xml.person[0].@first);
 
			// Clean with the double substr() method
			strCleanDoubleSubstr = cleanMasterStringDoubleSubstr(xml.person[0].@first);
 
			// Clean with an Object
			strCleanObject = cleanMasterStringObject(xml.person[0].@first);
 
			strCleanByteArray = cleanMasterStringByteArray(xml.person[0].@first);
 
			// Display the master string status every frame
			addEventListener(Event.ENTER_FRAME, onEnterFrame);
		}
 
		/**
		*   Replace a string's "master string"-- the string it was built from--
		*   with the same string plus a single character to save memory
		*   @param str String to clean
		*   @return The input string, but with a master string only one
		*           character larger than it
		*   @author Jackson Dunstan, JacksonDunstan.com
		*/
		public static function cleanMasterStringUnderscore(str:String): String
		{
			return ("_"+str).substr(1);
		}
 
		/**
		*   Replace a string's "master string"-- the string it was built from--
		*   with the string's first character to save memory
		*   @param str String to clean
		*   @return The input string, but with a single character master string
		*   @author Jackson Dunstan, JacksonDunstan.com
		*/
		public static function cleanMasterStringDoubleSubstr(str:String): String
		{
			return str.substr(0,1)+str.substr(1);
		}
 
		/**
		*   Try to replace a string's "master string"-- the string it was built
		*   from-- by using an Object. This function is not successful.
		*   @param str String to clean
		*   @return The input string, but with a single character master string
		*   @author Jackson Dunstan, JacksonDunstan.com
		*/
		public static function cleanMasterStringObject(str:String): String
		{
			var map:Object = {};
			map[str] = str;
			return str;
		}
 
		/**
		*   Replace a string's "master string"-- the string it was built from--
		*   with the string's first character to save memory
		*   @param str String to clean
		*   @return The input string, but with a single character master string
		*   @author Jackson Dunstan, JacksonDunstan.com
		*/
		public static function cleanMasterStringByteArray(str:String): String
		{
			var bytes:ByteArray = new ByteArray();
			bytes.writeUTFBytes(str);
			bytes.position = 0;
			return bytes.readUTFBytes(bytes.length);
		}
 
		private function onEnterFrame(ev:Event): void
		{
			function line(label:String, str:String): void
			{
				logger.appendText(label + ": " + getMasterString(str) + "\n");
			}
			logger.text = "Master strings:\n\n";
			line("Original", str);
			line("Clean (underscore)", strCleanUnderscore);
			line("Clean (double substr())", strCleanDoubleSubstr);
			line("Clean (Object)", strCleanObject);
			line("Clean (ByteArray)", strCleanByteArray);
		}
	}
}

Run the test

Here is what Flash Player 12.0 outputs:

Master strings:
 
Original: <people><person first="John"/></people>
Clean (underscore): _John
Clean (double substr()): J
Clean (Object): <people><person first="John"/></people>
Clean (ByteArray): null

First off, the Object version does not change the master string at all. I’m not sure why it would be expected to, other than some quirk or bug of the Flash Player.

Second, the underscore method continues working as before and serves as a decent baseline. It still dramatically cuts down on master string memory usage but ultimately ends up using memory for 2*N+1 characters for a string N characters long. For a big string, this could be a big hit.

The double substr() method helps this out a lot. The first substr() seems to end up as the master string. That’s good, because it’s only the first character of the string. In this case, we’re only using N+1 characters worth of memory: a huge improvement!

As good as the double substr() method is, it can’t beat the ByteArray solution. Here we end up with absolutely no master string. This is the solution to use to really clean your master string out and potentially save a ton of unnecessary memory usage.

Spot a bug? Have a question or suggestion? Post a comment!