// found in the LICENSE file.
define("mojo/public/js/bindings/codec", [
- "mojo/public/js/bindings/unicode"
-], function(unicode) {
+ "mojo/public/js/bindings/unicode",
+ "mojo/public/js/bindings/buffer"
+ ], function(unicode, buffer) {
var kErrorUnsigned = "Passing negative value to unsigned";
// Memory -------------------------------------------------------------------
var kAlignment = 8;
- var kHighWordMultiplier = 0x100000000;
- var kHostIsLittleEndian = (function () {
- var endianArrayBuffer = new ArrayBuffer(2);
- var endianUint8Array = new Uint8Array(endianArrayBuffer);
- var endianUint16Array = new Uint16Array(endianArrayBuffer);
- endianUint16Array[0] = 1;
- return endianUint8Array[0] == 1;
- })();
function align(size) {
return size + (kAlignment - (size % kAlignment)) % kAlignment;
}
- function getInt64(dataView, byteOffset, value) {
- var lo, hi;
- if (kHostIsLittleEndian) {
- lo = dataView.getUint32(byteOffset, kHostIsLittleEndian);
- hi = dataView.getInt32(byteOffset + 4, kHostIsLittleEndian);
- } else {
- hi = dataView.getInt32(byteOffset, kHostIsLittleEndian);
- lo = dataView.getUint32(byteOffset + 4, kHostIsLittleEndian);
- }
- return lo + hi * kHighWordMultiplier;
- }
-
- function getUint64(dataView, byteOffset, value) {
- var lo, hi;
- if (kHostIsLittleEndian) {
- lo = dataView.getUint32(byteOffset, kHostIsLittleEndian);
- hi = dataView.getUint32(byteOffset + 4, kHostIsLittleEndian);
- } else {
- hi = dataView.getUint32(byteOffset, kHostIsLittleEndian);
- lo = dataView.getUint32(byteOffset + 4, kHostIsLittleEndian);
- }
- return lo + hi * kHighWordMultiplier;
- }
-
- function setInt64(dataView, byteOffset, value) {
- var hi = Math.floor(value / kHighWordMultiplier);
- if (kHostIsLittleEndian) {
- dataView.setInt32(byteOffset, value, kHostIsLittleEndian);
- dataView.setInt32(byteOffset + 4, hi, kHostIsLittleEndian);
- } else {
- dataView.setInt32(byteOffset, hi, kHostIsLittleEndian);
- dataView.setInt32(byteOffset + 4, value, kHostIsLittleEndian);
- }
- }
-
- function setUint64(dataView, byteOffset, value) {
- var hi = (value / kHighWordMultiplier) | 0;
- if (kHostIsLittleEndian) {
- dataView.setInt32(byteOffset, value, kHostIsLittleEndian);
- dataView.setInt32(byteOffset + 4, hi, kHostIsLittleEndian);
- } else {
- dataView.setInt32(byteOffset, hi, kHostIsLittleEndian);
- dataView.setInt32(byteOffset + 4, value, kHostIsLittleEndian);
- }
+ function isAligned(offset) {
+ return offset >= 0 && (offset % kAlignment) === 0;
}
- function copyArrayBuffer(dstArrayBuffer, srcArrayBuffer) {
- (new Uint8Array(dstArrayBuffer)).set(new Uint8Array(srcArrayBuffer));
- }
-
- // Buffer -------------------------------------------------------------------
-
- function Buffer(sizeOrArrayBuffer) {
- if (sizeOrArrayBuffer instanceof ArrayBuffer) {
- this.arrayBuffer = sizeOrArrayBuffer;
- } else {
- this.arrayBuffer = new ArrayBuffer(sizeOrArrayBuffer);
- };
-
- this.dataView = new DataView(this.arrayBuffer);
- this.next = 0;
- }
-
- Buffer.prototype.alloc = function(size) {
- var pointer = this.next;
- this.next += size;
- if (this.next > this.arrayBuffer.byteLength) {
- var newSize = (1.5 * (this.arrayBuffer.byteLength + size)) | 0;
- this.grow(newSize);
- }
- return pointer;
- };
-
- Buffer.prototype.grow = function(size) {
- var newArrayBuffer = new ArrayBuffer(size);
- copyArrayBuffer(newArrayBuffer, this.arrayBuffer);
- this.arrayBuffer = newArrayBuffer;
- this.dataView = new DataView(this.arrayBuffer);
- };
-
- Buffer.prototype.trim = function() {
- this.arrayBuffer = this.arrayBuffer.slice(0, this.next);
- this.dataView = new DataView(this.arrayBuffer);
- };
-
// Constants ----------------------------------------------------------------
var kArrayHeaderSize = 8;
var kMessageHeaderSize = 16;
var kMessageWithRequestIDHeaderSize = 24;
+ var kStructHeaderNumBytesOffset = 0;
+ var kStructHeaderNumFieldsOffset = 4;
+
// Decoder ------------------------------------------------------------------
function Decoder(buffer, handles, base) {
};
Decoder.prototype.readInt8 = function() {
- var result = this.buffer.dataView.getInt8(this.next, kHostIsLittleEndian);
+ var result = this.buffer.getInt8(this.next);
this.next += 1;
return result;
};
Decoder.prototype.readUint8 = function() {
- var result = this.buffer.dataView.getUint8(this.next, kHostIsLittleEndian);
+ var result = this.buffer.getUint8(this.next);
this.next += 1;
return result;
};
Decoder.prototype.readInt16 = function() {
- var result = this.buffer.dataView.getInt16(this.next, kHostIsLittleEndian);
+ var result = this.buffer.getInt16(this.next);
this.next += 2;
return result;
};
Decoder.prototype.readUint16 = function() {
- var result = this.buffer.dataView.getUint16(this.next, kHostIsLittleEndian);
+ var result = this.buffer.getUint16(this.next);
this.next += 2;
return result;
};
Decoder.prototype.readInt32 = function() {
- var result = this.buffer.dataView.getInt32(this.next, kHostIsLittleEndian);
+ var result = this.buffer.getInt32(this.next);
this.next += 4;
return result;
};
Decoder.prototype.readUint32 = function() {
- var result = this.buffer.dataView.getUint32(this.next, kHostIsLittleEndian);
+ var result = this.buffer.getUint32(this.next);
this.next += 4;
return result;
};
Decoder.prototype.readInt64 = function() {
- var result = getInt64(this.buffer.dataView, this.next, kHostIsLittleEndian);
+ var result = this.buffer.getInt64(this.next);
this.next += 8;
return result;
};
Decoder.prototype.readUint64 = function() {
- var result = getUint64(
- this.buffer.dataView, this.next, kHostIsLittleEndian);
+ var result = this.buffer.getUint64(this.next);
this.next += 8;
return result;
};
Decoder.prototype.readFloat = function() {
- var result = this.buffer.dataView.getFloat32(
- this.next, kHostIsLittleEndian);
+ var result = this.buffer.getFloat32(this.next);
this.next += 4;
return result;
};
Decoder.prototype.readDouble = function() {
- var result = this.buffer.dataView.getFloat64(
- this.next, kHostIsLittleEndian);
+ var result = this.buffer.getFloat64(this.next);
this.next += 8;
return result;
};
var numberOfBytes = this.readUint32();
var numberOfElements = this.readUint32();
var val = new Array(numberOfElements);
- for (var i = 0; i < numberOfElements; ++i) {
- val[i] = cls.decode(this);
+ if (cls.cls === PackedBool) {
+ var byte;
+ for (var i = 0; i < numberOfElements; ++i) {
+ if (i % 8 === 0)
+ byte = this.readUint8();
+ val[i] = (byte & (1 << i % 8)) ? true : false;
+ }
+ } else {
+ for (var i = 0; i < numberOfElements; ++i) {
+ val[i] = cls.decode(this);
+ }
}
return val;
};
};
Encoder.prototype.writeInt8 = function(val) {
- // NOTE: Endianness doesn't come into play for single bytes.
- this.buffer.dataView.setInt8(this.next, val);
+ this.buffer.setInt8(this.next, val);
this.next += 1;
};
if (val < 0) {
throw new Error(kErrorUnsigned);
}
- // NOTE: Endianness doesn't come into play for single bytes.
- this.buffer.dataView.setUint8(this.next, val);
+ this.buffer.setUint8(this.next, val);
this.next += 1;
};
Encoder.prototype.writeInt16 = function(val) {
- this.buffer.dataView.setInt16(this.next, val, kHostIsLittleEndian);
+ this.buffer.setInt16(this.next, val);
this.next += 2;
};
if (val < 0) {
throw new Error(kErrorUnsigned);
}
- this.buffer.dataView.setUint16(this.next, val, kHostIsLittleEndian);
+ this.buffer.setUint16(this.next, val);
this.next += 2;
};
Encoder.prototype.writeInt32 = function(val) {
- this.buffer.dataView.setInt32(this.next, val, kHostIsLittleEndian);
+ this.buffer.setInt32(this.next, val);
this.next += 4;
};
if (val < 0) {
throw new Error(kErrorUnsigned);
}
- this.buffer.dataView.setUint32(this.next, val, kHostIsLittleEndian);
+ this.buffer.setUint32(this.next, val);
this.next += 4;
};
Encoder.prototype.writeInt64 = function(val) {
- setInt64(this.buffer.dataView, this.next, val);
+ this.buffer.setInt64(this.next, val);
this.next += 8;
};
if (val < 0) {
throw new Error(kErrorUnsigned);
}
- setUint64(this.buffer.dataView, this.next, val);
+ this.buffer.setUint64(this.next, val);
this.next += 8;
};
Encoder.prototype.writeFloat = function(val) {
- this.buffer.dataView.setFloat32(this.next, val, kHostIsLittleEndian);
+ this.buffer.setFloat32(this.next, val);
this.next += 4;
};
Encoder.prototype.writeDouble = function(val) {
- this.buffer.dataView.setFloat64(this.next, val, kHostIsLittleEndian);
+ this.buffer.setFloat64(this.next, val);
this.next += 8;
};
this.next += numberOfElements;
};
- Encoder.prototype.encodeArray = function(cls, val) {
- var numberOfElements = val.length;
- var numberOfBytes = kArrayHeaderSize + cls.encodedSize * numberOfElements;
- this.writeUint32(numberOfBytes);
+ Encoder.prototype.encodeArray =
+ function(cls, val, numberOfElements, encodedSize) {
+ if (numberOfElements === undefined)
+ numberOfElements = val.length;
+ if (encodedSize === undefined)
+ encodedSize = kArrayHeaderSize + cls.encodedSize * numberOfElements;
+
+ this.writeUint32(encodedSize);
this.writeUint32(numberOfElements);
- for (var i = 0; i < numberOfElements; ++i) {
- cls.encode(this, val[i]);
+
+ if (cls.cls === PackedBool) {
+ var byte = 0;
+ for (i = 0; i < numberOfElements; ++i) {
+ if (val[i])
+ byte |= (1 << i % 8);
+ if (i % 8 === 7 || i == numberOfElements - 1) {
+ Uint8.encode(this, byte);
+ byte = 0;
+ }
+ }
+ } else {
+ for (var i = 0; i < numberOfElements; ++i)
+ cls.encode(this, val[i]);
}
};
this.encodePointer(val);
return;
}
- var encodedSize = kArrayHeaderSize + cls.encodedSize * val.length;
+ var numberOfElements = val.length;
+ var encodedSize = kArrayHeaderSize + ((cls.cls === PackedBool) ?
+ Math.ceil(numberOfElements / 8) : cls.encodedSize * numberOfElements);
var encoder = this.createAndEncodeEncoder(encodedSize);
- encoder.encodeArray(cls, val);
+ encoder.encodeArray(cls, val, numberOfElements, encodedSize);
};
Encoder.prototype.encodeStringPointer = function(val) {
// Message ------------------------------------------------------------------
+ var kMessageNameOffset = kStructHeaderSize;
+ var kMessageFlagsOffset = kMessageNameOffset + 4;
+ var kMessageRequestIDOffset = kMessageFlagsOffset + 4;
+
var kMessageExpectsResponse = 1 << 0;
var kMessageIsResponse = 1 << 1;
- // Skip over num_bytes, num_fields, and message_name.
- var kFlagsOffset = 4 + 4 + 4;
-
- // Skip over num_bytes, num_fields, message_name, and flags.
- var kRequestIDOffset = 4 + 4 + 4 + 4;
-
function Message(buffer, handles) {
this.buffer = buffer;
this.handles = handles;
}
- Message.prototype.setRequestID = function(requestID) {
- // TODO(darin): Verify that space was reserved for this field!
- setUint64(this.buffer.dataView, kRequestIDOffset, requestID);
+ Message.prototype.getHeaderNumBytes = function() {
+ return this.buffer.getUint32(kStructHeaderNumBytesOffset);
+ };
+
+ Message.prototype.getHeaderNumFields = function() {
+ return this.buffer.getUint32(kStructHeaderNumFieldsOffset);
};
Message.prototype.getFlags = function() {
- return this.buffer.dataView.getUint32(kFlagsOffset, kHostIsLittleEndian);
+ return this.buffer.getUint32(kMessageFlagsOffset);
+ };
+
+ Message.prototype.isResponse = function() {
+ return (this.getFlags() & kMessageIsResponse) != 0;
};
+ Message.prototype.expectsResponse = function() {
+ return (this.getFlags() & kMessageExpectsResponse) != 0;
+ };
+
+ Message.prototype.setRequestID = function(requestID) {
+ // TODO(darin): Verify that space was reserved for this field!
+ this.buffer.setUint64(kMessageRequestIDOffset, requestID);
+ };
+
+
// MessageBuilder -----------------------------------------------------------
function MessageBuilder(messageName, payloadSize) {
// Currently, we don't compute the payload size correctly ahead of time.
// Instead, we resize the buffer at the end.
var numberOfBytes = kMessageHeaderSize + payloadSize;
- this.buffer = new Buffer(numberOfBytes);
+ this.buffer = new buffer.Buffer(numberOfBytes);
this.handles = [];
var encoder = this.createEncoder(kMessageHeaderSize);
encoder.writeUint32(kMessageHeaderSize);
// Currently, we don't compute the payload size correctly ahead of time.
// Instead, we resize the buffer at the end.
var numberOfBytes = kMessageWithRequestIDHeaderSize + payloadSize;
- this.buffer = new Buffer(numberOfBytes);
+ this.buffer = new buffer.Buffer(numberOfBytes);
this.handles = [];
var encoder = this.createEncoder(kMessageWithRequestIDHeaderSize);
encoder.writeUint32(kMessageWithRequestIDHeaderSize);
function MessageReader(message) {
this.decoder = new Decoder(message.buffer, message.handles, 0);
var messageHeaderSize = this.decoder.readUint32();
- this.payloadSize =
- message.buffer.arrayBuffer.byteLength - messageHeaderSize;
+ this.payloadSize = message.buffer.byteLength - messageHeaderSize;
var numFields = this.decoder.readUint32();
this.messageName = this.decoder.readUint32();
this.flags = this.decoder.readUint32();
// Built-in types -----------------------------------------------------------
+ // This type is only used with ArrayOf(PackedBool).
+ function PackedBool() {
+ }
+
function Int8() {
}
var exports = {};
exports.align = align;
- exports.Buffer = Buffer;
+ exports.isAligned = isAligned;
exports.Message = Message;
exports.MessageBuilder = MessageBuilder;
exports.MessageWithRequestIDBuilder = MessageWithRequestIDBuilder;
exports.kArrayHeaderSize = kArrayHeaderSize;
exports.kStructHeaderSize = kStructHeaderSize;
exports.kMessageHeaderSize = kMessageHeaderSize;
+ exports.kMessageWithRequestIDHeaderSize = kMessageWithRequestIDHeaderSize;
exports.kMessageExpectsResponse = kMessageExpectsResponse;
exports.kMessageIsResponse = kMessageIsResponse;
exports.Int8 = Int8;
exports.String = String;
exports.PointerTo = PointerTo;
exports.ArrayOf = ArrayOf;
+ exports.PackedBool = PackedBool;
exports.Handle = Handle;
return exports;
});