Some work on streaming.

git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@3855 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Ray Greenwell
2006-02-15 01:41:14 +00:00
parent 28ac720b98
commit 1d36299e3c
13 changed files with 216 additions and 175 deletions
+7
View File
@@ -51,3 +51,10 @@ Notes
- constructors do not defaultly call super()- be sure to do it explicitely. - constructors do not defaultly call super()- be sure to do it explicitely.
Maybe we should get in the habit of doing it in Java for consistency and Maybe we should get in the habit of doing it in Java for consistency and
explicitness. explicitness.
- I'm a little shaky still about how I'm going to handle arrays. In
ActionScript all arrays (except ByteArray) are the same type: Array.
If class A is extended by B and C, the server could pass around an array of
A, filled with B and C elements. I would have no way on the client to
inspect an array like that and know to tell the server that it's an A[].
Punting completely on Arrays for now.
+45
View File
@@ -1,6 +1,51 @@
package com.threerings.io { package com.threerings.io {
import flash.util.ByteArray;
public class ArrayMask public class ArrayMask
implements Streamable
{ {
public function ArrayMask (length :int = 0)
{
var mlength :int = length / 8;
if (length % 8 != 0) {
mlength++;
}
_mask.length = mlength;
}
/**
* Set the specified index as containing a non-null element in the
* array we're representing.
*/
public function setBit (index :int) :void
{
_mask[index/8] |= (1 << (index % 8));
}
/**
* Is the specified array element non-null?
*/
public function isSet (index :int) :Boolean
{
return (_mask[index/8] & (1 << (index % 8))) != 0;
}
// documentation inherited from interface Streamable
public function writeObject (out :ObjectOutputStream) :void
{
out.writeShort(_mask.length);
out.writeBytes(_mask);
}
// documentation inherited from interface Streamable
public function readObject (ins :ObjectInputStream) :void
{
_mask.length = ins.readShort();
ins.readBytes(_mask, 0, _mask.length);
}
/** The array mask. */
protected var _mask :ByteArray = new ByteArray();
} }
} }
+38 -24
View File
@@ -29,8 +29,6 @@ public class ObjectInputStream
public function readObject () :* public function readObject () :*
//throws IOError //throws IOError
{ {
var cmap :ClassMapping;
try { try {
// read in the class code for this instance // read in the class code for this instance
var code :int = readShort(); var code :int = readShort();
@@ -38,16 +36,19 @@ public class ObjectInputStream
// a zero code indicates a null value // a zero code indicates a null value
if (code == 0) { if (code == 0) {
return null; return null;
}
var cmap :ClassMapping;
// if the code is negative, that means we've never seen it // if the code is negative, that means we've never seen it
// before and class metadata follows // before and class metadata follows
} else if (code < 0) { if (code < 0) {
// first swap the code into positive land // first swap the code into positive land
code *= -1; code *= -1;
// read in the class metadata // read in the class metadata
var cname :String = readUTF(); var cname :String = readUTF();
var streamer :Streamer = Streamer.getStreamer(cname); var streamer :Streamer = Streamer.getStreamerByJavaName(cname);
cmap = new ClassMapping(code, cname, streamer); cmap = new ClassMapping(code, cname, streamer);
_classMap[code] = cmap; _classMap[code] = cmap;
@@ -60,34 +61,41 @@ public class ObjectInputStream
} }
} }
// create an instance of the appropriate object var target :*;
_streamer = cmap.streamer; if (cmap.streamer === null) {
var target :* = _streamer.createObject(this); var clazz :Class = flash.util.getClassByName(cmap.cname);
_current = target; target = new clazz();
// now read the instance data } else {
_streamer.readObject(target, this, true); target = cmap.streamer.createObject(this);
}
// and return the newly read object readBareObjectImpl(target, cmap.streamer);
return target; return target;
} finally { } catch (MemoryError me) {
// clear out our current object references throw new IOError("out of memory" + me.message);
_current = null;
_streamer = null;
} }
} }
public function readBareObject (obj :*) :void public function readBareObject (obj :*) :void
//throws IOError //throws IOError
{ {
try { readBareObjectImpl(obj, Streamer.getStreamer(obj));
// obtain the streamer for the objects of this class }
_streamer = Streamer.getStreamer(ClassUtil.getClassName(obj));
_current = obj;
// now read the instance data protected function readBareObjectImpl (obj :*, streamer :Streamer) :void
_streamer.readObject(obj, this, true); {
// streamable objects
if (streamer == null) {
var sable :Streamable = (obj as Streamable);
sable.readObject(this);
return;
}
_current = obj;
_streamer = steamer;
try {
_streamer.readObject(obj, this);
} finally { } finally {
// clear out our current object references // clear out our current object references
@@ -125,9 +133,15 @@ public class ObjectInputStream
} }
public function readBytes (bytes :ByteArray, offset :uint = 0, public function readBytes (bytes :ByteArray, offset :uint = 0,
length :uint = 0) :void length :uint = undefined) :void
//throws IOError //throws IOError
{ {
// IDataInput reads all available bytes if a length is not passed
// in. Protect against an easy error to make by using the length of
// the array
if (length === undefined) {
length = bytes.length;
}
_source.readBytes(bytes, offset, length); _source.readBytes(bytes, offset, length);
} }
@@ -180,6 +194,6 @@ public class ObjectInputStream
protected var _streamer :Streamer; protected var _streamer :Streamer;
/** A map of short class code to ClassMapping info. */ /** A map of short class code to ClassMapping info. */
protected var _classMap :SimpleMap = new SimpleMap(); protected var _classMap :Array = new Array();
} }
} }
+38 -41
View File
@@ -7,7 +7,6 @@ import com.threerings.util.ClassUtil;
import com.threerings.util.SimpleMap; import com.threerings.util.SimpleMap;
public class ObjectOutputStream public class ObjectOutputStream
// implements IDataOutput
{ {
public function ObjectOutputStream (targ :IDataOutput) public function ObjectOutputStream (targ :IDataOutput)
{ {
@@ -23,57 +22,55 @@ public class ObjectOutputStream
return; return;
} }
try { var cname :String = ClassUtil.getClassName(obj);
var cname :String = ClassUtil.getClassName(obj); // look up the class mapping record
var cmap :ClassMapping = _classMap[cname];
// look up the class mapping record // create a class mapping if we've not got one
var cmap :ClassMapping = _classMap[cname]; if (cmap === undefined) {
_current = obj; var streamer :Streamer = Streamer.getStreamer(obj);
if (streamer === undefined) {
// create a class mapping if we've not got one // TODO
if (cmap === undefined) { trace("OMG, cannot stream ", cname);
var streamer :Streamer = Streamer.getStreamer(cname); return;
if (streamer === undefined) {
// TODO
trace("OMG, cannot stream ", cname);
return;
}
cmap = new ClassMapping(_nextCode++, cname, streamer);
_classMap[cname] = cmap;
// TODO: if _nextCode blows short, log an error
writeShort(-cmap.code);
writeUTF(cname);
} else {
writeShort(cmap.code);
} }
// now write the instance data cmap = new ClassMapping(_nextCode++, cname, streamer);
_streamer = cmap.streamer; _classMap[cname] = cmap;
_streamer.writeObject(obj, this, true);
} finally { // TODO: if _nextCode blows short, log an error
_current = null;
_streamer = null; writeShort(-cmap.code);
writeUTF((streamer == null) ? cname : streamer.getJavaClassName());
} else {
writeShort(cmap.code);
} }
writeBareObjectImpl(obj, cmap.streamer);
} }
public function writeBareObject (obj :*) :void public function writeBareObject (obj :*) :void
//throws IOError //throws IOError
{ {
writeBareObjectImpl(obj, Streamer.getStreamer(obj));
}
protected function writeBareObjectImpl (obj :*, streamer :Streamer)
{
// if it's Streamable, it goes straight through
if (streamer == null) {
var sable :Streamable = (obj as Streamable);
sable.writeObject(this);
return;
}
// otherwise, stream it!
_current = obj;
_streamer = streamer;
try { try {
// get the streamer for objects of this type _streamer.writeObject(obj, this);
_streamer = Streamer.getStreamer(ClassUtil.getClassName(obj));
_current = obj;
// now write the instance data
_streamer.writeObject(obj, this, true);
} finally { } finally {
// clear out our current object references
_current = null; _current = null;
_streamer = null; _streamer = null;
} }
@@ -147,7 +144,7 @@ public class ObjectOutputStream
public function writeShort (value :int) :void public function writeShort (value :int) :void
//throws IOError //throws IOError
{ {
_targ.writeInt(value); _targ.writeShort(value);
} }
public function writeUTF (value :String) :void public function writeUTF (value :String) :void
+59 -78
View File
@@ -32,87 +32,76 @@ public class Streamer
public static function getStreamer (obj :*) :Streamer public static function getStreamer (obj :*) :Streamer
{ {
if (_streamerMap == null) { if (obj is Streamable) {
return null;
}
if (_streamers == null) {
createStreamers(); createStreamers();
} }
if (obj is Streamable) { var s :Streamer;
return null; for (var ii :int = 0; ii < _streamers.lenght; ii++) {
s = (_streamers[ii] as Streamer);
} else if (obj is String) { if (s.isStreamerFor(obj)) {
return STRING_STREAMER; return s;
}
} else if (obj is int) {
return INT_STREAMER;
} else if (obj is Number) {
return NUMBER_STREAMER;
} else if (obj is Array) {
return ARRAY_STREAMER;
} else if (obj is ByteArray) {
return BYTE_ARRAY_STREAMER;
} else {
return undefined;
} }
return undefined;
} }
public static function getStreamerByJavaName (jname :String) :Streamer public static function getStreamerByJavaName (jname :String) :Streamer
{ {
if (jname === "java.lang.String") { if (_streamers == null) {
return STRING_STREAMER; createStreamers();
} else if (jname === "java.lang.Integer") {
return INT_STREAMER;
} else if (jname === "java.lang.Double") {
return NUMBER_STREAMER;
} else if (jname === "[Ljava.lang.Object") {
return ARRAY_STREAMER;
} else if (jname === "[B") {
return BYTE_ARRAY_STREAMER;
} else {
return null;
} }
var s :Streamer;
for (var ii :int = 0; ii < _streamers.length; ii++) {
s = (_streamers[ii] as Streamer);
if (s.getJavaClassName() === jname) {
return s;
}
}
return null;
} }
/** This should be a protected constructor. */ /** This should be a protected constructor. */
public function Streamer (targ :Class) public function Streamer (targ :Class, jname :String)
//throws IOError //throws IOError
{ {
_targ = targ; _targ = targ;
_jname = jname;
}
public function isStreamerFor (obj :*) :Boolean
{
return (obj is _targ); // scripting langs are weird
} }
/** /**
* 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.
*/ */
// TODO public function getJavaClassName () :String
public function getClassName () :String
{ {
return "TODO: javaname"; return _jname;
} }
public function writeObject (obj :*, out :ObjectOutputStream, public function writeObject (obj :*, out :ObjectOutputStream) :void
useWriter :Boolean) :void
//throws IOError //throws IOError
{ {
// TODO: check use of isProtoTypeOf, it's unclear if it's trace("TODO");
// going to do what we want
if (useWriter && obj.isPrototypeOf(Streamable)) { if (obj is Array) {
obj.writeObject(out); trace("Arrays not yet done. Crap!");
return; /**
var arr :Array = (obj as Array); // not strictly necessary
var length :int = arr.length;
out.writeInt(length);
*/
} }
// TODO: cope with arrays
// write out the fields... this is bogus because
// we just want to call the streamer method
obj.writeObject(out);
} }
public function createObject (ins :ObjectInputStream) :* public function createObject (ins :ObjectInputStream) :*
@@ -122,22 +111,10 @@ public class Streamer
return new _targ(); return new _targ();
} }
public function readObject (obj :*, ins :ObjectInputStream, public function readObject (obj :*, ins :ObjectInputStream) :void
useReader :Boolean) :void
//throws IOError //throws IOError
{ {
// TODO: check use of isProtoTypeOf, it's unclear if it's trace("TODO");
// going to do what we want
if (useReader && obj.isPrototypeOf(Streamable)) {
obj.readObject(ins);
return;
}
// TODO: cope with arrays
// read in the fields... this is bogus because we just
// want to call the streamer method
obj.readObject(ins);
} }
/** /**
@@ -146,19 +123,23 @@ public class Streamer
*/ */
protected static function createStreamers () :void protected static function createStreamers () :void
{ {
_streamerMap = new SimpleMap(); _streamers = new Array();
// add our default streamers
_streamers.push(
new StringStreamer(),
new IntStreamer(),
new NumberStreamer(),
new ArrayStreamer(),
new ByteArrayStreamer()
);
} }
protected var _targ :Class; protected var _targ :Class;
protected static var _streamerMap :SimpleMap; protected var _jname :String;
/** Just a list of our standard streamers. */
protected static const STRING_STREAMER :Streamer = new StringStreamer(); protected static var _streamers :Array;
protected static const BYTE_ARRAY_STREAMER :Streamer =
new ByteArrayStreamer();
protected static const ARRAY_STREAMER :Streamer = new ArrayStreamer();
protected static const INT_STREAMER :Streamer = new IntStreamer();
protected static const NUMBER_STREAMER :Streamer = new NumberStreamer();
} }
} }
@@ -11,7 +11,7 @@ public class ArrayStreamer extends Streamer
{ {
public function ArrayStreamer () public function ArrayStreamer ()
{ {
super(Array); super(Array, "[Ljava.lang.Object");
} }
public override function createObject (ins :ObjectInputStream) :* public override function createObject (ins :ObjectInputStream) :*
@@ -19,8 +19,7 @@ public class ArrayStreamer extends Streamer
return new Array(ins.readInt()); return new Array(ins.readInt());
} }
public override function writeObject (obj :*, out :ObjectOutputStream, public override function writeObject (obj :*, out :ObjectOutputStream) :void
useWriter :Boolean) :void
{ {
var arr :Array = (obj as Array); var arr :Array = (obj as Array);
out.writeInt(arr.length); out.writeInt(arr.length);
@@ -29,8 +28,7 @@ public class ArrayStreamer extends Streamer
} }
} }
public override function readObject (obj :*, ins :ObjectInputStream, public override function readObject (obj :*, ins :ObjectInputStream) :void
useReader :Boolean) :void
{ {
var arr :Array = (obj as Array); var arr :Array = (obj as Array);
for (var ii :int = 0; ii < arr.length; ii++) { for (var ii :int = 0; ii < arr.length; ii++) {
@@ -13,7 +13,7 @@ public class ByteArrayStreamer extends Streamer
{ {
public function ByteArrayStreamer () public function ByteArrayStreamer ()
{ {
super(ByteArray); super(ByteArray, "[B"); // yes, that's the Java class for a byte[].
} }
public override function createObject (ins :ObjectInputStream) :* public override function createObject (ins :ObjectInputStream) :*
@@ -23,16 +23,14 @@ public class ByteArrayStreamer extends Streamer
return bytes; return bytes;
} }
public override function writeObject (obj :*, out :ObjectOutputStream, public override function writeObject (obj :*, out :ObjectOutputStream) :void
useWriter :Boolean) :void
{ {
var bytes :ByteArray = (obj as ByteArray); var bytes :ByteArray = (obj as ByteArray);
out.writeInt(bytes.length); out.writeInt(bytes.length);
out.writeBytes(bytes); out.writeBytes(bytes);
} }
public override function readObject (obj :*, ins :ObjectInputStream, public override function readObject (obj :*, ins :ObjectInputStream) :void
useReader :Boolean) :void
{ {
var bytes :ByteArray = (obj as ByteArray); var bytes :ByteArray = (obj as ByteArray);
ins.readBytes(bytes, 0, bytes.length); ins.readBytes(bytes, 0, bytes.length);
@@ -11,7 +11,7 @@ public class IntStreamer extends Streamer
{ {
public function IntStreamer () public function IntStreamer ()
{ {
super(int); super(int, "java.lang.Integer");
} }
public override function createObject (ins :ObjectInputStream) :* public override function createObject (ins :ObjectInputStream) :*
@@ -19,15 +19,13 @@ public class IntStreamer extends Streamer
return ins.readInt(); return ins.readInt();
} }
public override function writeObject (obj :*, out :ObjectOutputStream, public override function writeObject (obj :*, out :ObjectOutputStream) :void
useWriter :Boolean) :void
{ {
var i :int = (obj as int); var i :int = (obj as int);
out.writeInt(i); out.writeInt(i);
} }
public override function readObject (obj :*, ins :ObjectInputStream, public override function readObject (obj :*, ins :ObjectInputStream) :void
useReader :Boolean) :void
{ {
// nothing here, the int is fully read in createObject() // nothing here, the int is fully read in createObject()
} }
@@ -11,7 +11,7 @@ public class NumberStreamer extends Streamer
{ {
public function NumberStreamer () public function NumberStreamer ()
{ {
super(Number); super(Number, "java.lang.Double");
} }
public override function createObject (ins :ObjectInputStream) :* public override function createObject (ins :ObjectInputStream) :*
@@ -19,15 +19,13 @@ public class NumberStreamer extends Streamer
return ins.readDouble(); return ins.readDouble();
} }
public override function writeObject (obj :*, out :ObjectOutputStream, public override function writeObject (obj :*, out :ObjectOutputStream) :void
useWriter :Boolean) :void
{ {
var n :Number = (obj as Number); var n :Number = (obj as Number);
out.writeDouble(n); out.writeDouble(n);
} }
public override function readObject (obj :*, ins :ObjectInputStream, public override function readObject (obj :*, ins :ObjectInputStream) :void
useReader :Boolean) :void
{ {
// nothing here, the Number is fully read in createObject() // nothing here, the Number is fully read in createObject()
} }
@@ -11,7 +11,7 @@ public class StringStreamer extends Streamer
{ {
public function StringStreamer () public function StringStreamer ()
{ {
super(String); super(String, "java.lang.String");
} }
public override function createObject (ins :ObjectInputStream) :* public override function createObject (ins :ObjectInputStream) :*
@@ -19,15 +19,13 @@ public class StringStreamer extends Streamer
return ins.readUTF(); return ins.readUTF();
} }
public override function writeObject (obj :*, out :ObjectOutputStream, public override function writeObject (obj :*, out :ObjectOutputStream) :void
useWriter :Boolean) :void
{ {
var s :String = (obj as String); var s :String = (obj as String);
out.writeUTF(s); out.writeUTF(s);
} }
public override function readObject (obj :*, ins :ObjectInputStream, public override function readObject (obj :*, ins :ObjectInputStream) :void
useReader :Boolean) :void
{ {
// nothing here, the String is fully read in createObject() // nothing here, the String is fully read in createObject()
} }
@@ -21,6 +21,7 @@ public class Communicator
{ {
// create the socket and set up listeners // create the socket and set up listeners
_socket = new Socket(); _socket = new Socket();
_socket.endian = Endian.BIG_ENDIAN;
_socket.addEventListener(Event.CONNECT, socketOpened); _socket.addEventListener(Event.CONNECT, socketOpened);
_socket.addEventListener(IOErrorEvent.IO_ERROR, socketError); _socket.addEventListener(IOErrorEvent.IO_ERROR, socketError);
_socket.addEventListener(Event.CLOSE, socketClosed); _socket.addEventListener(Event.CLOSE, socketClosed);
+12 -4
View File
@@ -1,12 +1,20 @@
package com.threerings.util { package com.threerings.util {
import flash.util.*; public class ClassUtil
public class ClassUtil extends Object
{ {
public static function getClassName (obj :*) :String public static function getClassName (obj :*) :String
{ {
return getQualifiedClassName(obj).replace("::", "."); return flash.util.getQualifiedClassName(obj).replace("::", ".");
}
public static function getClass (obj :*) :Class
{
return flash.util.getClassByName(getClassName(obj));
}
public static function getClassByName (cname :String) :Class
{
return flash.util.getClassByName(cname);
} }
} }
} }
+1 -3
View File
@@ -30,7 +30,7 @@ public class Name extends Object
return Name.isBlank(this); return Name.isBlank(this);
} }
public /*override*/ function toString () :String public override function toString () :String
{ {
return _name; return _name;
} }
@@ -65,8 +65,6 @@ public class Name extends Object
out.writeUTF(_name); out.writeUTF(_name);
} }
*/ */
} }
// documentation inherited from interface Streamable // documentation inherited from interface Streamable