Refactor to avoid linear lookup of streamers.

This was a bit of a rabbit hole and took longer than expected,
and I had a stupid bug that took a while to find,
but in the end there are way more lines of code removed than
added, so I'll call it a win. Streamer lookup is now via a Dictionary.


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@5608 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Ray Greenwell
2008-12-30 23:25:50 +00:00
parent c7612ecb7d
commit e6ea84e5da
5 changed files with 50 additions and 92 deletions
@@ -72,7 +72,7 @@ public class ObjectOutputStream
} }
if (ObjectInputStream.DEBUG) { if (ObjectInputStream.DEBUG) {
log.debug("Assigning class code [code=" + cmap.code + ", class=" + cname + "]."); log.debug("Assigning class code", "code", cmap.code, "class", cname);
} }
writeShort(-cmap.code); writeShort(-cmap.code);
+40 -62
View File
@@ -22,6 +22,7 @@
package com.threerings.io { package com.threerings.io {
import flash.utils.ByteArray; import flash.utils.ByteArray;
import flash.utils.Dictionary;
import com.threerings.util.ClassUtil; import com.threerings.util.ClassUtil;
import com.threerings.util.Enum; import com.threerings.util.Enum;
@@ -41,33 +42,11 @@ public class Streamer
{ {
public static function getStreamer (obj :Object) :Streamer public static function getStreamer (obj :Object) :Streamer
{ {
initStreamers();
var streamer :Streamer;
for each (streamer in _streamers) {
if (streamer.isStreamerFor(obj)) {
return streamer;
}
}
// from here on out we're creating new streamers
if (obj is TypedArray) { if (obj is TypedArray) {
streamer = new ArrayStreamer((obj as TypedArray).getJavaType()); return getStreamerByJavaName(TypedArray(obj).getJavaType());
} else if (obj is Enum) {
streamer = new EnumStreamer(ClassUtil.getClass(obj));
} else if (obj is Streamable) {
streamer = new Streamer(ClassUtil.getClass(obj));
} else { } else {
return null; return getStreamerByClass(ClassUtil.getClass(obj));
} }
// add the new streamer and return it
_streamers.push(streamer);
return streamer;
} }
public static function getStreamerByClass (clazz :Class) :Streamer public static function getStreamerByClass (clazz :Class) :Streamer
@@ -78,11 +57,9 @@ public class Streamer
throw new Error("Broken, TODO"); throw new Error("Broken, TODO");
} }
var streamer :Streamer; var streamer :Streamer = _byClass[clazz] as Streamer;
for each (streamer in _streamers) { if (streamer != null) {
if (streamer.isStreamerForClass(clazz)) { return streamer;
return streamer;
}
} }
if (ClassUtil.isAssignableAs(Enum, clazz)) { if (ClassUtil.isAssignableAs(Enum, clazz)) {
@@ -96,7 +73,7 @@ public class Streamer
} }
// add the new streamer and return it // add the new streamer and return it
_streamers.push(streamer); registerStreamer(streamer);
return streamer; return streamer;
} }
@@ -109,11 +86,9 @@ public class Streamer
} }
// see if we have a streamer for it // see if we have a streamer for it
var streamer :Streamer; var streamer :Streamer = _byJName[jname] as Streamer;
for each (streamer in _streamers) { if (streamer != null) {
if (streamer.getJavaClassName() === jname) { return streamer;
return streamer;
}
} }
// see if it's an array that we unstream using an ArrayStreamer // see if it's an array that we unstream using an ArrayStreamer
@@ -136,7 +111,7 @@ public class Streamer
} }
// add the good new streamer // add the good new streamer
_streamers.push(streamer); registerStreamer(streamer);
return streamer; return streamer;
} }
@@ -148,16 +123,6 @@ public class Streamer
_jname = (jname != null) ? jname : Translations.getToServer(ClassUtil.getClassName(targ)); _jname = (jname != null) ? jname : Translations.getToServer(ClassUtil.getClassName(targ));
} }
public function isStreamerFor (obj :Object) :Boolean
{
return isStreamerForClass(ClassUtil.getClass(obj));
}
public function isStreamerForClass (clazz :Class) :Boolean
{
return (clazz == _target);
}
/** /**
* Return the String to use to identify the class that we're streaming. * Return the String to use to identify the class that we're streaming.
*/ */
@@ -166,6 +131,14 @@ public class Streamer
return _jname; return _jname;
} }
/**
* Return the Class identifying that which we stream, or null to not be registered.
*/
public function getClass () :Class
{
return _target;
}
public function writeObject (obj :Object, out :ObjectOutputStream) :void public function writeObject (obj :Object, out :ObjectOutputStream) :void
//throws IOError //throws IOError
{ {
@@ -185,6 +158,16 @@ public class Streamer
(obj as Streamable).readObject(ins); (obj as Streamable).readObject(ins);
} }
protected static function registerStreamer (st :Streamer) :void
{
_byJName[st.getJavaClassName()] = st;
trace(": registered " + st.getJavaClassName());
var c :Class = st.getClass();
if (c != null) {
_byClass[c] = st;
}
}
/** /**
* Initialize our streamers. This cannot simply be done statically * Initialize our streamers. This cannot simply be done statically
* because we cannot instantiate a subclass when this class is still * because we cannot instantiate a subclass when this class is still
@@ -192,20 +175,15 @@ public class Streamer
*/ */
private static function initStreamers () :void private static function initStreamers () :void
{ {
if (_streamers == null) { if (_byJName != null) {
// Init like this so that _streamers is not null asap. return;
_streamers = []; }
// We could add each one by one, so that any later streamers _byJName = new Dictionary();
// can find and use the earlier ones as delegates. _byClass = new Dictionary();
_streamers.push(new StringStreamer(), for each (var c :Class in
new NumberStreamer(), [ StringStreamer, NumberStreamer, ByteStreamer, IntegerStreamer, LongStreamer,
new ByteStreamer(), FloatStreamer, ArrayStreamer, ByteArrayStreamer ]) {
new ShortStreamer(), registerStreamer(Streamer(new c()));
new IntegerStreamer(),
new LongStreamer(),
new FloatStreamer(),
new ArrayStreamer(),
new ByteArrayStreamer());
} }
} }
@@ -213,7 +191,7 @@ public class Streamer
protected var _jname :String; protected var _jname :String;
/** Just a list of our standard streamers. */ protected static var _byClass :Dictionary;
protected static var _streamers :Array; protected static var _byJName :Dictionary;
} }
} }
+4
View File
@@ -56,6 +56,10 @@ public class Translations
// initialize some standard classes // initialize some standard classes
addTranslation("Object", "java.lang.Object"); addTranslation("Object", "java.lang.Object");
addTranslation("String", "java.lang.String"); addTranslation("String", "java.lang.String");
addTranslation("flash.utils.ByteArray", "[B");
addTranslation("com.threerings.util.langBoolean", "java.lang.Boolean"); addTranslation("com.threerings.util.langBoolean", "java.lang.Boolean");
addTranslation("com.threerings.util.Byte", "java.lang.Byte");
addTranslation("com.threerings.util.Integer", "java.lang.Integer");
addTranslation("com.threerings.util.Float", "java.lang.Float");
} }
} }
@@ -61,35 +61,16 @@ public class ArrayStreamer extends Streamer
_elementType = Boolean; _elementType = Boolean;
} else { } else {
Log.getLog(this).warning("Other array types are " + Log.getLog(this).warning("Other array types are not yet handled", "jname", jname);
"currently not handled yet [jname=" + jname + "].");
throw new Error("Don't know how to stream '" + jname + "' instances."); throw new Error("Don't know how to stream '" + jname + "' instances.");
} }
} }
override public function isStreamerFor (obj :Object) :Boolean override public function getClass () :Class
{ {
if (obj is TypedArray) { // this is a little weird, but basically, we don't want to be registered for Array
// TypedArrays need the same element type // unless we're the basic Object array streamer.
return ((obj as TypedArray).getJavaType() === _jname); return (_jname == "[Ljava.lang.Object;") ? Array : null;
} else {
// any other array is streamed as Object[]
return (obj is Array) && (_jname === "[Ljava.lang.Object;");
}
}
override public function isStreamerForClass (clazz :Class) :Boolean
{
if (clazz == TypedArray) {
return false; // TODO: we're kinda fucked for finding a streamer
// by class for TypedArrays here. The caller should be passing
// the java name.
} else {
return ClassUtil.isAssignableAs(Array, clazz) &&
(_jname === "[Ljava.lang.Object;");
}
} }
override public function createObject (ins :ObjectInputStream) :Object override public function createObject (ins :ObjectInputStream) :Object
@@ -37,11 +37,6 @@ public class LongStreamer extends Streamer
super(Long, "java.lang.Long"); super(Long, "java.lang.Long");
} }
override public function createObject (ins :ObjectInputStream) :Object
{
return new Long();
}
override public function writeObject (obj :Object, out :ObjectOutputStream) :void override public function writeObject (obj :Object, out :ObjectOutputStream) :void
{ {
var longy :Long = (obj as Long); var longy :Long = (obj as Long);