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.
Maybe we should get in the habit of doing it in Java for consistency and
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 {
import flash.util.ByteArray;
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 () :*
//throws IOError
{
var cmap :ClassMapping;
try {
// read in the class code for this instance
var code :int = readShort();
@@ -38,16 +36,19 @@ public class ObjectInputStream
// a zero code indicates a null value
if (code == 0) {
return null;
}
var cmap :ClassMapping;
// if the code is negative, that means we've never seen it
// before and class metadata follows
} else if (code < 0) {
if (code < 0) {
// first swap the code into positive land
code *= -1;
// read in the class metadata
var cname :String = readUTF();
var streamer :Streamer = Streamer.getStreamer(cname);
var streamer :Streamer = Streamer.getStreamerByJavaName(cname);
cmap = new ClassMapping(code, cname, streamer);
_classMap[code] = cmap;
@@ -60,34 +61,41 @@ public class ObjectInputStream
}
}
// create an instance of the appropriate object
_streamer = cmap.streamer;
var target :* = _streamer.createObject(this);
_current = target;
var target :*;
if (cmap.streamer === null) {
var clazz :Class = flash.util.getClassByName(cmap.cname);
target = new clazz();
// now read the instance data
_streamer.readObject(target, this, true);
// and return the newly read object
} else {
target = cmap.streamer.createObject(this);
}
readBareObjectImpl(target, cmap.streamer);
return target;
} finally {
// clear out our current object references
_current = null;
_streamer = null;
} catch (MemoryError me) {
throw new IOError("out of memory" + me.message);
}
}
public function readBareObject (obj :*) :void
//throws IOError
{
try {
// obtain the streamer for the objects of this class
_streamer = Streamer.getStreamer(ClassUtil.getClassName(obj));
_current = obj;
readBareObjectImpl(obj, Streamer.getStreamer(obj));
}
// now read the instance data
_streamer.readObject(obj, this, true);
protected function readBareObjectImpl (obj :*, streamer :Streamer) :void
{
// 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 {
// clear out our current object references
@@ -125,9 +133,15 @@ public class ObjectInputStream
}
public function readBytes (bytes :ByteArray, offset :uint = 0,
length :uint = 0) :void
length :uint = undefined) :void
//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);
}
@@ -180,6 +194,6 @@ public class ObjectInputStream
protected var _streamer :Streamer;
/** 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;
public class ObjectOutputStream
// implements IDataOutput
{
public function ObjectOutputStream (targ :IDataOutput)
{
@@ -23,57 +22,55 @@ public class ObjectOutputStream
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
var cmap :ClassMapping = _classMap[cname];
_current = obj;
// create a class mapping if we've not got one
if (cmap === undefined) {
var streamer :Streamer = Streamer.getStreamer(cname);
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);
// create a class mapping if we've not got one
if (cmap === undefined) {
var streamer :Streamer = Streamer.getStreamer(obj);
if (streamer === undefined) {
// TODO
trace("OMG, cannot stream ", cname);
return;
}
// now write the instance data
_streamer = cmap.streamer;
_streamer.writeObject(obj, this, true);
cmap = new ClassMapping(_nextCode++, cname, streamer);
_classMap[cname] = cmap;
} finally {
_current = null;
_streamer = null;
// TODO: if _nextCode blows short, log an error
writeShort(-cmap.code);
writeUTF((streamer == null) ? cname : streamer.getJavaClassName());
} else {
writeShort(cmap.code);
}
writeBareObjectImpl(obj, cmap.streamer);
}
public function writeBareObject (obj :*) :void
//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 {
// get the streamer for objects of this type
_streamer = Streamer.getStreamer(ClassUtil.getClassName(obj));
_current = obj;
// now write the instance data
_streamer.writeObject(obj, this, true);
_streamer.writeObject(obj, this);
} finally {
// clear out our current object references
_current = null;
_streamer = null;
}
@@ -147,7 +144,7 @@ public class ObjectOutputStream
public function writeShort (value :int) :void
//throws IOError
{
_targ.writeInt(value);
_targ.writeShort(value);
}
public function writeUTF (value :String) :void
+59 -78
View File
@@ -32,87 +32,76 @@ public class Streamer
public static function getStreamer (obj :*) :Streamer
{
if (_streamerMap == null) {
if (obj is Streamable) {
return null;
}
if (_streamers == null) {
createStreamers();
}
if (obj is Streamable) {
return null;
} else if (obj is String) {
return STRING_STREAMER;
} 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;
var s :Streamer;
for (var ii :int = 0; ii < _streamers.lenght; ii++) {
s = (_streamers[ii] as Streamer);
if (s.isStreamerFor(obj)) {
return s;
}
}
return undefined;
}
public static function getStreamerByJavaName (jname :String) :Streamer
{
if (jname === "java.lang.String") {
return STRING_STREAMER;
} 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;
if (_streamers == null) {
createStreamers();
}
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. */
public function Streamer (targ :Class)
public function Streamer (targ :Class, jname :String)
//throws IOError
{
_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.
*/
// TODO
public function getClassName () :String
public function getJavaClassName () :String
{
return "TODO: javaname";
return _jname;
}
public function writeObject (obj :*, out :ObjectOutputStream,
useWriter :Boolean) :void
public function writeObject (obj :*, out :ObjectOutputStream) :void
//throws IOError
{
// TODO: check use of isProtoTypeOf, it's unclear if it's
// going to do what we want
if (useWriter && obj.isPrototypeOf(Streamable)) {
obj.writeObject(out);
return;
trace("TODO");
if (obj is Array) {
trace("Arrays not yet done. Crap!");
/**
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) :*
@@ -122,22 +111,10 @@ public class Streamer
return new _targ();
}
public function readObject (obj :*, ins :ObjectInputStream,
useReader :Boolean) :void
public function readObject (obj :*, ins :ObjectInputStream) :void
//throws IOError
{
// TODO: check use of isProtoTypeOf, it's unclear if it's
// 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);
trace("TODO");
}
/**
@@ -146,19 +123,23 @@ public class Streamer
*/
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 static var _streamerMap :SimpleMap;
protected var _jname :String;
protected static const STRING_STREAMER :Streamer = new StringStreamer();
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();
/** Just a list of our standard streamers. */
protected static var _streamers :Array;
}
}
@@ -11,7 +11,7 @@ public class ArrayStreamer extends Streamer
{
public function ArrayStreamer ()
{
super(Array);
super(Array, "[Ljava.lang.Object");
}
public override function createObject (ins :ObjectInputStream) :*
@@ -19,8 +19,7 @@ public class ArrayStreamer extends Streamer
return new Array(ins.readInt());
}
public override function writeObject (obj :*, out :ObjectOutputStream,
useWriter :Boolean) :void
public override function writeObject (obj :*, out :ObjectOutputStream) :void
{
var arr :Array = (obj as Array);
out.writeInt(arr.length);
@@ -29,8 +28,7 @@ public class ArrayStreamer extends Streamer
}
}
public override function readObject (obj :*, ins :ObjectInputStream,
useReader :Boolean) :void
public override function readObject (obj :*, ins :ObjectInputStream) :void
{
var arr :Array = (obj as Array);
for (var ii :int = 0; ii < arr.length; ii++) {
@@ -13,7 +13,7 @@ public class ByteArrayStreamer extends Streamer
{
public function ByteArrayStreamer ()
{
super(ByteArray);
super(ByteArray, "[B"); // yes, that's the Java class for a byte[].
}
public override function createObject (ins :ObjectInputStream) :*
@@ -23,16 +23,14 @@ public class ByteArrayStreamer extends Streamer
return bytes;
}
public override function writeObject (obj :*, out :ObjectOutputStream,
useWriter :Boolean) :void
public override function writeObject (obj :*, out :ObjectOutputStream) :void
{
var bytes :ByteArray = (obj as ByteArray);
out.writeInt(bytes.length);
out.writeBytes(bytes);
}
public override function readObject (obj :*, ins :ObjectInputStream,
useReader :Boolean) :void
public override function readObject (obj :*, ins :ObjectInputStream) :void
{
var bytes :ByteArray = (obj as ByteArray);
ins.readBytes(bytes, 0, bytes.length);
@@ -11,7 +11,7 @@ public class IntStreamer extends Streamer
{
public function IntStreamer ()
{
super(int);
super(int, "java.lang.Integer");
}
public override function createObject (ins :ObjectInputStream) :*
@@ -19,15 +19,13 @@ public class IntStreamer extends Streamer
return ins.readInt();
}
public override function writeObject (obj :*, out :ObjectOutputStream,
useWriter :Boolean) :void
public override function writeObject (obj :*, out :ObjectOutputStream) :void
{
var i :int = (obj as int);
out.writeInt(i);
}
public override function readObject (obj :*, ins :ObjectInputStream,
useReader :Boolean) :void
public override function readObject (obj :*, ins :ObjectInputStream) :void
{
// nothing here, the int is fully read in createObject()
}
@@ -11,7 +11,7 @@ public class NumberStreamer extends Streamer
{
public function NumberStreamer ()
{
super(Number);
super(Number, "java.lang.Double");
}
public override function createObject (ins :ObjectInputStream) :*
@@ -19,15 +19,13 @@ public class NumberStreamer extends Streamer
return ins.readDouble();
}
public override function writeObject (obj :*, out :ObjectOutputStream,
useWriter :Boolean) :void
public override function writeObject (obj :*, out :ObjectOutputStream) :void
{
var n :Number = (obj as Number);
out.writeDouble(n);
}
public override function readObject (obj :*, ins :ObjectInputStream,
useReader :Boolean) :void
public override function readObject (obj :*, ins :ObjectInputStream) :void
{
// nothing here, the Number is fully read in createObject()
}
@@ -11,7 +11,7 @@ public class StringStreamer extends Streamer
{
public function StringStreamer ()
{
super(String);
super(String, "java.lang.String");
}
public override function createObject (ins :ObjectInputStream) :*
@@ -19,15 +19,13 @@ public class StringStreamer extends Streamer
return ins.readUTF();
}
public override function writeObject (obj :*, out :ObjectOutputStream,
useWriter :Boolean) :void
public override function writeObject (obj :*, out :ObjectOutputStream) :void
{
var s :String = (obj as String);
out.writeUTF(s);
}
public override function readObject (obj :*, ins :ObjectInputStream,
useReader :Boolean) :void
public override function readObject (obj :*, ins :ObjectInputStream) :void
{
// nothing here, the String is fully read in createObject()
}
@@ -21,6 +21,7 @@ public class Communicator
{
// create the socket and set up listeners
_socket = new Socket();
_socket.endian = Endian.BIG_ENDIAN;
_socket.addEventListener(Event.CONNECT, socketOpened);
_socket.addEventListener(IOErrorEvent.IO_ERROR, socketError);
_socket.addEventListener(Event.CLOSE, socketClosed);
+12 -4
View File
@@ -1,12 +1,20 @@
package com.threerings.util {
import flash.util.*;
public class ClassUtil extends Object
public class ClassUtil
{
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);
}
public /*override*/ function toString () :String
public override function toString () :String
{
return _name;
}
@@ -65,8 +65,6 @@ public class Name extends Object
out.writeUTF(_name);
}
*/
}
// documentation inherited from interface Streamable