Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
65edaa8a57 | ||
|
|
27ef02fac9 | ||
|
|
39743ab28a | ||
|
|
1c39e04adf | ||
|
|
dcf1b10278 | ||
|
|
ccdb72c931 | ||
|
|
09c79de07d | ||
|
|
b013752ed5 | ||
|
|
6b899a4250 |
No files matched your search
@@ -26,12 +26,21 @@ var Promise = require("es6-promise").Promise;
|
||||
var spawn = require("child_process").spawn;
|
||||
|
||||
var goldenBinary;
|
||||
var goldenPackedBinary;
|
||||
var goldenFlatBinary;
|
||||
var goldenPackedFlatBinary;
|
||||
try {
|
||||
// Works in Ekam build.
|
||||
goldenBinary = fs.readFileSync("node-capnp/testdata/binary");
|
||||
goldenPackedBinary = fs.readFileSync("node-capnp/testdata/packedbinary");
|
||||
goldenFlatBinary = fs.readFileSync("node-capnp/testdata/flat");
|
||||
goldenPackedFlatBinary = fs.readFileSync("node-capnp/testdata/packedflat");
|
||||
} catch (ex) {
|
||||
// Works in npm build.
|
||||
goldenBinary = fs.readFileSync("src/node-capnp/testdata/binary");
|
||||
goldenPackedBinary = fs.readFileSync("src/node-capnp/testdata/packedbinary");
|
||||
goldenFlatBinary = fs.readFileSync("src/node-capnp/testdata/flat");
|
||||
goldenPackedFlatBinary = fs.readFileSync("src/node-capnp/testdata/packedflat");
|
||||
}
|
||||
|
||||
var test = require("./test.capnp");
|
||||
@@ -54,6 +63,31 @@ assert.equal("foo", test.TestConstants.textConst);
|
||||
assert.equal("baz", test.TestConstants.structConst.textField);
|
||||
assert.equal("xyzzy", test.TestConstants.textListConst[1]);
|
||||
|
||||
// Test packed serialization/deserialization
|
||||
|
||||
var parsedPacked = capnp.parsePacked(test.TestAllTypes, goldenPackedBinary);
|
||||
var roundTrippedPacked = capnp.serializePacked(test.TestAllTypes, parsedPacked);
|
||||
assert.equal(goldenPackedBinary.length, roundTrippedPacked.length, "Round trip changed size?");
|
||||
assert.equal(goldenPackedBinary.toString("base64"), roundTrippedPacked.toString("base64"), "Round trip lost data?");
|
||||
|
||||
var parsedFlat = capnp.parse(test.TestAllTypes, goldenFlatBinary, {flat: true});
|
||||
var roundTrippedFlat = capnp.serialize(test.TestAllTypes, parsedFlat, {flat: true});
|
||||
assert.equal(goldenFlatBinary.length, roundTrippedFlat.length, "Round trip changed size?");
|
||||
assert.equal(goldenFlatBinary.toString("base64"), roundTrippedFlat.toString("base64"), "Round trip lost data?");
|
||||
|
||||
var parsedPackedFlat = capnp.parse(test.TestAllTypes, goldenPackedFlatBinary, {packed: true, flat: true});
|
||||
var roundTrippedPackedFlat = capnp.serialize(test.TestAllTypes, parsedPackedFlat, {packed: true, flat: true});
|
||||
assert.equal(goldenPackedFlatBinary.length, roundTrippedPackedFlat.length, "Round trip changed size?");
|
||||
assert.equal(goldenPackedFlatBinary.toString("base64"), roundTrippedPackedFlat.toString("base64"), "Round trip lost data?");
|
||||
|
||||
// TODO(someday): do a more thorough deep equality comparison of parsed and parsedPacked
|
||||
var keys = ["voidField", "boolField", "int8Field", "int16Field", "int32Field", "int64Field"];
|
||||
for (var key in keys) {
|
||||
assert.equal(parsed[key], parsedPacked[key]);
|
||||
assert.equal(parsed[key], parsedFlat[key]);
|
||||
assert.equal(parsed[key], parsedPackedFlat[key]);
|
||||
}
|
||||
|
||||
// =======================================================================================
|
||||
|
||||
/*
|
||||
|
||||
+117
-15
@@ -34,12 +34,14 @@
|
||||
#include <uv.h>
|
||||
#include <kj/async.h>
|
||||
#include <kj/async-io.h>
|
||||
#include <kj/io.h>
|
||||
#include <kj/vector.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <capnp/rpc-twoparty.h>
|
||||
#include <capnp/rpc.capnp.h>
|
||||
#include <capnp/serialize.h>
|
||||
#include <capnp/serialize-packed.h>
|
||||
#include <unordered_map>
|
||||
#include <inttypes.h>
|
||||
#include <set>
|
||||
@@ -741,6 +743,7 @@ v8::Local<v8::Value> toJsException(kj::Exception&& exception) {
|
||||
|
||||
kj::Exception fromJsException(v8::Handle<v8::Value> exception) {
|
||||
kj::Exception::Type type = kj::Exception::Type::FAILED;
|
||||
kj::String description;
|
||||
|
||||
if (exception->IsObject()) {
|
||||
v8::HandleScope scope;
|
||||
@@ -756,9 +759,16 @@ kj::Exception fromJsException(v8::Handle<v8::Value> exception) {
|
||||
type = kj::Exception::Type::UNIMPLEMENTED;
|
||||
}
|
||||
}
|
||||
|
||||
v8::Handle<v8::Value> stack = obj->Get(v8::String::NewSymbol("stack"));
|
||||
if (!stack.IsEmpty() && !stack->IsUndefined()) {
|
||||
description = toKjString(stack);
|
||||
} else {
|
||||
description = toKjString(exception);
|
||||
}
|
||||
}
|
||||
|
||||
return kj::Exception(type, "(javascript)", 0, toKjString(exception));
|
||||
return kj::Exception(type, "(javascript)", 0, kj::mv(description));
|
||||
}
|
||||
|
||||
EmptyHandle throwTypeError(kj::StringPtr name, const std::type_info& type,
|
||||
@@ -1112,6 +1122,26 @@ struct StructReader {
|
||||
: message(data), root(message.getRoot<capnp::DynamicStruct>(schema)) {}
|
||||
};
|
||||
|
||||
struct FlatStructReader {
|
||||
kj::ArrayPtr<const capnp::word> segments[1];
|
||||
capnp::SegmentArrayMessageReader message;
|
||||
capnp::DynamicStruct::Reader root;
|
||||
|
||||
FlatStructReader(kj::ArrayPtr<const capnp::word> data, capnp::StructSchema schema)
|
||||
: segments{data}, message(segments),
|
||||
root(message.getRoot<capnp::DynamicStruct>(schema)) {}
|
||||
};
|
||||
|
||||
struct PackedStructReader {
|
||||
kj::ArrayInputStream inputStream;
|
||||
capnp::PackedMessageReader message;
|
||||
capnp::DynamicStruct::Reader root;
|
||||
|
||||
PackedStructReader(kj::ArrayPtr<const byte> bytes, capnp::StructSchema schema)
|
||||
: inputStream(bytes), message(inputStream),
|
||||
root(message.getRoot<capnp::DynamicStruct>(schema)) {}
|
||||
};
|
||||
|
||||
struct ServerRequest {
|
||||
kj::Own<kj::PromiseFulfiller<void>> fulfiller;
|
||||
// Fulfill to complete the call. You must null out the pointers below, as well as
|
||||
@@ -1137,6 +1167,10 @@ kj::Maybe<capnp::DynamicStruct::Reader> unwrapReader(v8::Handle<v8::Value> handl
|
||||
});
|
||||
} else KJ_IF_MAYBE(reader, Wrapper::tryUnwrap<StructReader>(handle)) {
|
||||
return reader->root;
|
||||
} else KJ_IF_MAYBE(reader, Wrapper::tryUnwrap<PackedStructReader>(handle)) {
|
||||
return reader->root;
|
||||
} else KJ_IF_MAYBE(reader, Wrapper::tryUnwrap<FlatStructReader>(handle)) {
|
||||
return reader->root;
|
||||
} else KJ_IF_MAYBE(request, Wrapper::tryUnwrap<ServerRequest>(handle)) {
|
||||
return request->params;
|
||||
} else KJ_IF_MAYBE(builder, unwrapBuilder(handle)) {
|
||||
@@ -1695,7 +1729,7 @@ v8::Handle<v8::Value> toJsParams(const v8::Arguments& args) {
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
v8::Handle<v8::Value> fromBytes(const v8::Arguments& args) {
|
||||
// fromBytes(buffer, schema) -> reader
|
||||
// fromBytes(buffer, schema, options) -> reader
|
||||
|
||||
KJV8_UNWRAP(CapnpContext, context, args.Data());
|
||||
|
||||
@@ -1708,33 +1742,92 @@ v8::Handle<v8::Value> fromBytes(const v8::Arguments& args) {
|
||||
return emptyHandle;
|
||||
}
|
||||
|
||||
if (!args[2]->IsObject()) {
|
||||
KJV8_TYPE_ERROR(obj, v8::Object);
|
||||
}
|
||||
|
||||
return liftKj([&]() -> v8::Handle<v8::Value> {
|
||||
kj::ArrayPtr<const capnp::word> words;
|
||||
if (reinterpret_cast<uintptr_t>(buffer.begin()) % sizeof(capnp::word) != 0) {
|
||||
// Array is not aligned. We have to make a copy. :(
|
||||
auto array = kj::heapArray<capnp::word>(buffer.size() / sizeof(capnp::word));
|
||||
memcpy(array.begin(), buffer.begin(), buffer.size());
|
||||
words = array;
|
||||
bufferHandle = context.wrapper.wrapCopy(kj::mv(array));
|
||||
auto options = args[2].As<v8::Object>();
|
||||
bool packed = options->Get(v8::String::NewSymbol("packed"))->BooleanValue();
|
||||
bool flat = options->Get(v8::String::NewSymbol("flat"))->BooleanValue();
|
||||
|
||||
v8::Local<v8::Object> wrapper;
|
||||
|
||||
if (packed) {
|
||||
if (flat) {
|
||||
auto bytes = kj::arrayPtr(buffer.begin(), buffer.size());
|
||||
auto words = kj::heapArray<capnp::word>(capnp::computeUnpackedSizeInWords(bytes));
|
||||
kj::ArrayInputStream input(bytes);
|
||||
capnp::_::PackedInputStream unpacker(input);
|
||||
unpacker.read(words.asBytes().begin(), words.asBytes().size());
|
||||
wrapper = context.wrapper.wrap(new FlatStructReader(words, schema.asStruct()));
|
||||
bufferHandle = context.wrapper.wrapCopy(kj::mv(words));
|
||||
} else {
|
||||
wrapper = context.wrapper.wrap(new PackedStructReader(
|
||||
kj::arrayPtr(buffer.begin(), buffer.size()), schema.asStruct()));
|
||||
}
|
||||
} else {
|
||||
// Yay, array is aligned.
|
||||
words = kj::arrayPtr(reinterpret_cast<const capnp::word*>(buffer.begin()),
|
||||
buffer.size() / sizeof(capnp::word));
|
||||
kj::ArrayPtr<const capnp::word> words;
|
||||
if (reinterpret_cast<uintptr_t>(buffer.begin()) % sizeof(capnp::word) != 0) {
|
||||
// Array is not aligned. We have to make a copy. :(
|
||||
auto array = kj::heapArray<capnp::word>(buffer.size() / sizeof(capnp::word));
|
||||
memcpy(array.begin(), buffer.begin(), buffer.size());
|
||||
words = array;
|
||||
bufferHandle = context.wrapper.wrapCopy(kj::mv(array));
|
||||
} else {
|
||||
// Yay, array is aligned.
|
||||
words = kj::arrayPtr(reinterpret_cast<const capnp::word*>(buffer.begin()),
|
||||
buffer.size() / sizeof(capnp::word));
|
||||
}
|
||||
|
||||
if (flat) {
|
||||
wrapper = context.wrapper.wrap(new FlatStructReader(words, schema.asStruct()));
|
||||
} else {
|
||||
wrapper = context.wrapper.wrap(new StructReader(words, schema.asStruct()));
|
||||
}
|
||||
}
|
||||
|
||||
auto wrapper = context.wrapper.wrap(new StructReader(words, schema.asStruct()));
|
||||
wrapper->SetHiddenValue(v8::String::NewSymbol("buffer"), bufferHandle);
|
||||
return wrapper;
|
||||
});
|
||||
}
|
||||
|
||||
v8::Handle<v8::Value> toBytes(const v8::Arguments& args) {
|
||||
// toBytes(builder) -> buffer
|
||||
// toBytes(builder, options) -> buffer
|
||||
|
||||
KJV8_UNWRAP(StructBuilder, builder, args[0]);
|
||||
if (!args[1]->IsObject()) {
|
||||
KJV8_TYPE_ERROR(obj, v8::Object);
|
||||
}
|
||||
|
||||
return liftKj([&]() -> v8::Handle<v8::Value> {
|
||||
return wrapBuffer(capnp::messageToFlatArray(builder.message));
|
||||
auto options = args[1].As<v8::Object>();
|
||||
bool packed = options->Get(v8::String::NewSymbol("packed"))->BooleanValue();
|
||||
bool flat = options->Get(v8::String::NewSymbol("flat"))->BooleanValue();
|
||||
|
||||
if (flat) {
|
||||
// Write whole message to flat array.
|
||||
auto root = builder.root.asReader();
|
||||
auto words = kj::heapArray<capnp::word>(root.totalSize().wordCount + 1);
|
||||
memset(words.asBytes().begin(), 0, words.asBytes().size());
|
||||
capnp::copyToUnchecked(root, words);
|
||||
|
||||
if (packed) {
|
||||
kj::VectorOutputStream output;
|
||||
capnp::_::PackedOutputStream packer(output);
|
||||
packer.write(words.asBytes().begin(), words.asBytes().size());
|
||||
return wrapBuffer(kj::heapArray(output.getArray()));
|
||||
} else {
|
||||
return wrapBuffer(kj::mv(words));
|
||||
}
|
||||
} else if (packed) {
|
||||
kj::VectorOutputStream output;
|
||||
capnp::writePackedMessage(output, builder.message);
|
||||
auto packedMessage = heapArray(output.getArray());
|
||||
return wrapBuffer(kj::mv(packedMessage));
|
||||
} else {
|
||||
return wrapBuffer(capnp::messageToFlatArray(builder.message));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2255,7 +2348,16 @@ v8::Handle<v8::Value> fulfillPromisedCap(const v8::Arguments& args) {
|
||||
KJV8_UNWRAP(capnp::DynamicCapability::Client, cap, args[1]);
|
||||
|
||||
return liftKj([&]() -> v8::Handle<v8::Value> {
|
||||
auto schema = cap.getSchema();
|
||||
|
||||
fulfiller->fulfill(kj::mv(cap));
|
||||
|
||||
// Avoid segfault if capability is called again.
|
||||
cap = capnp::Capability::Client(
|
||||
capnp::newBrokenCap(KJ_EXCEPTION(DISCONNECTED,
|
||||
"Capability was used to resolve a promise capability and so has been closed.")))
|
||||
.castAs<capnp::DynamicCapability>(schema);
|
||||
|
||||
return v8::Undefined();
|
||||
});
|
||||
}
|
||||
|
||||
+17
-7
@@ -256,23 +256,33 @@ exports.connect = function (addr, bootstrapCap) {
|
||||
return new Connection(v8capnp.connect(addr, bootstrapCap));
|
||||
}
|
||||
|
||||
exports.parse = function (schema, buffer) {
|
||||
var reader = v8capnp.fromBytes(buffer, schema);
|
||||
exports.parse = function (schema, buffer, options) {
|
||||
var reader = v8capnp.fromBytes(buffer, schema, options || {});
|
||||
return v8capnp.toJs(reader, Capability);
|
||||
}
|
||||
|
||||
exports.serialize = function (schema, value) {
|
||||
exports.parsePacked = function (schema, buffer) {
|
||||
// TODO(cleanup): Remove in favor of options.
|
||||
return exports.parse(schema, buffer, {packed: true});
|
||||
}
|
||||
|
||||
exports.serialize = function (schema, value, options) {
|
||||
var builder = v8capnp.newBuilder(schema);
|
||||
v8capnp.fromJs(builder, value, LocalCapWrapper);
|
||||
return v8capnp.toBytes(builder);
|
||||
return v8capnp.toBytes(builder, options || {});
|
||||
}
|
||||
|
||||
exports.serializePacked = function (schema, value) {
|
||||
// TODO(cleanup): Remove in favor of options.
|
||||
return exports.serialize(schema, value, {packed: true});
|
||||
}
|
||||
|
||||
exports.Capability = Capability;
|
||||
|
||||
exports.bytesToPreorder = function(schema, buf) {
|
||||
exports.bytesToPreorder = function(schema, buf, options) {
|
||||
// Parse, copy, serialize, to get a preorder traversal.
|
||||
var reader = v8capnp.fromBytes(buf, schema);
|
||||
var reader = v8capnp.fromBytes(buf, schema, options || {});
|
||||
var builder = v8capnp.copyBuilder(reader);
|
||||
return v8capnp.toBytes(builder);
|
||||
return v8capnp.toBytes(builder, options || {});
|
||||
}
|
||||
|
||||
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Reference in new issue
Block a user