diff --git a/src/as/com/threerings/io/streamers/LongStreamer.as b/src/as/com/threerings/io/streamers/LongStreamer.as index 2b636c273..3af76711c 100644 --- a/src/as/com/threerings/io/streamers/LongStreamer.as +++ b/src/as/com/threerings/io/streamers/LongStreamer.as @@ -39,23 +39,21 @@ public class LongStreamer extends Streamer override public function createObject (ins :ObjectInputStream) :Object { - return new Long(0); + return new Long(); } override public function writeObject (obj :Object, out :ObjectOutputStream) :void { var longy :Long = (obj as Long); - out.writeInt(longy.low); - out.writeInt(longy.high); + out.writeBytes(longy.bytes, 0, longy.bytes.length); } override public function readObject (obj :Object, ins :ObjectInputStream) :void { var longy :Long = (obj as Long); - longy.low = ins.readInt(); - longy.high = ins.readInt(); + ins.readBytes(longy.bytes, 0, longy.bytes.length); } } } diff --git a/src/as/com/threerings/presents/data/ConMgrStats.as b/src/as/com/threerings/presents/data/ConMgrStats.as index 8cc062bb3..cf4f5e9f4 100644 --- a/src/as/com/threerings/presents/data/ConMgrStats.as +++ b/src/as/com/threerings/presents/data/ConMgrStats.as @@ -87,8 +87,8 @@ public class ConMgrStats extends SimpleStreamableObject overQueueSize = ins.readInt(); connects = ins.readInt(); disconnects = ins.readInt(); - bytesIn = new Long(ins.readInt(), ins.readInt()); - bytesOut = new Long(ins.readInt(), ins.readInt()); + bytesIn = ins.readField(Long) as Long; + bytesOut = ins.readField(Long) as Long; msgsIn = ins.readInt(); msgsOut = ins.readInt(); } @@ -103,10 +103,8 @@ public class ConMgrStats extends SimpleStreamableObject out.writeInt(overQueueSize); out.writeInt(connects); out.writeInt(disconnects); - out.writeInt(bytesIn == null ? 0 : bytesIn.low); - out.writeInt(bytesIn == null ? 0 : bytesIn.high); - out.writeInt(bytesOut == null ? 0 : bytesOut.low); - out.writeInt(bytesOut == null ? 0 : bytesOut.high); + out.writeField(bytesIn); + out.writeField(bytesOut); out.writeInt(msgsIn); out.writeInt(msgsOut); } diff --git a/src/as/com/threerings/presents/net/PongResponse.as b/src/as/com/threerings/presents/net/PongResponse.as index cc0bd1a79..d0fbe8c53 100644 --- a/src/as/com/threerings/presents/net/PongResponse.as +++ b/src/as/com/threerings/presents/net/PongResponse.as @@ -57,7 +57,7 @@ public class PongResponse extends DownstreamMessage super.readObject(ins); // TODO: Figure out how we're really going to cope with longs - _packStamp = new Long(0); + _packStamp = new Long(); ins.readBareObject(_packStamp); _processDelay = ins.readInt(); diff --git a/src/as/com/threerings/util/Long.as b/src/as/com/threerings/util/Long.as index a03141633..313eb26c5 100644 --- a/src/as/com/threerings/util/Long.as +++ b/src/as/com/threerings/util/Long.as @@ -21,40 +21,95 @@ package com.threerings.util { +import flash.utils.ByteArray; +import flash.utils.Endian; + /** * Equivalent to java.lang.Long. */ public class Long implements Equalable { - public var high :int; - public var low :int; + public var bytes :ByteArray; - public static function valueOf (low :int, high :int = 0) :Long + public function Long () { - return new Long(low, high); + bytes = new ByteArray(); + bytes.endian = Endian.BIG_ENDIAN; + bytes.writeDouble(0); + bytes.position = 0; } - public function Long (low :int, high :int = 0) + /** + * Creates a new Long from the provided variable. Only integers in the [-2^63, 2^63) range + * can be converted; non-integer values in this range will be rounded, and values outside + * of the range will trigger an ArgumentError. Additionally, since Number is a + * double-precision floating point value, values outside of the [-2^52, 2^52) range + * will suffer loss of precision. + */ + public static function fromNumber (value :Number = 0) :Long { - this.low = low; - this.high = high; + if (value < -9223372036854775808 || value >= 9223372036854775808 || + isNaN(value) || !isFinite(value)) { + throw new ArgumentError("Out of range initialization value for Long: " + value); + } + + var n :Number = Math.round(value); + var l :Long = new Long(); + for (var ii :int = 7; ii >= 0; ii--) { + l.bytes[ii] = (n % 256); + n = Math.floor(n / 256); + } + return l; + } + + /** + * Creates a new Number from this Long variable. Since Number is a double-precision + * floating point type, values outside the [-2^52, 2^52) range will lose precision. + */ + public function toNumber () :Number + { + var n :Number = 0; + var positive :Boolean = ((bytes[0] & 0x80) == 0x00); + for (var ii :int = 0; ii < 8; ii++) { + // if the number is negative, complement each byte as it comes in, and fix up later + n = n * 256 + (positive ? bytes[ii] : (255 - bytes[ii])); + } + // now fix up negative numbers + if (! positive) { + n = -(n + 1); + } + return n; + } + + public function toString () :String + { + var s :String = "Long [0x "; + for (var ii :int = 0; ii < 8; ii++) { + // my kingdom for a hex formatting routine! + if (ii != 0) { s += " "; } + if (bytes[ii] < 16) { s += "0"; } + s += int(bytes[ii]).toString(16); + } + s += "]"; + return s; } // from Equalable public function equals (other :Object) :Boolean { - if (!(other is Long)) { + var that :Long = (other as Long); + if (that == null || this.bytes.length != 8 || that.bytes.length != 8) { return false; } - var that :Long = (other as Long); - return (this.high == that.high) && (this.low == that.low); - } - // from Wrapped, except that we don't implement Wrapped anymore - public function unwrap () :Object - { - return low; // TODO + // byte-wise comparison + for (var ii :int = 0; ii < 8; ii++) { + if (this.bytes[ii] != that.bytes[ii]) { + return false; + } + } + return true; } } }