From: Piotr Kosko/Tizen API (PLT) /SRPOL/Engineer/Samsung Electronics
Date: Thu, 26 Aug 2021 07:53:19 +0000 (+0200)
Subject: [ML] Added binary communication in getTensorRawData()
X-Git-Tag: submit/tizen/20210901.122723~1
X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fchanges%2F10%2F263110%2F7;p=platform%2Fcore%2Fapi%2Fwebapi-plugins.git
[ML] Added binary communication in getTensorRawData()
[Verification]
TCT passrate for tct-ml, tct-mlpipeline and tct-single 100% passrate
Performance results tested with below scenario for
224x224x3 tensor size (TM1 device):
var tries = 100;
var sum = 0;
var tensorsInfo = new tizen.ml.TensorsInfo();
tensorsInfo.addTensorInfo("tensor", "UINT8", [224, 224, 3]);
var tensorsData = tensorsInfo.getTensorsData();
var start = new Date();
tensorsData.setTensorRawData(0, rgb);
for (var i = 0; i < tries; ++i) {
var start = new Date();
tensorsData.getTensorRawData(0)
var end = new Date();
sum += end-start;
}
console.log ("Average time is: " + (sum / tries) );
BEFORE: Average time is: 139.53
AFTER: Average time is: 5.93
Change-Id: I47f369319744e64c3bd961eb7dda4ad8823b1494
Signed-off-by: DongHyun Song
Signed-off-by: Piotr Kosko/Tizen API (PLT) /SRPOL/Engineer/Samsung Electronics
---
diff --git a/doc/src/assets/webapi-plugins-devel-test/src/tool/desc_gentool.cc b/doc/src/assets/webapi-plugins-devel-test/src/tool/desc_gentool.cc
index 93804f9f..a001736b 100644
--- a/doc/src/assets/webapi-plugins-devel-test/src/tool/desc_gentool.cc
+++ b/doc/src/assets/webapi-plugins-devel-test/src/tool/desc_gentool.cc
@@ -117,11 +117,10 @@ const void* get_interface(const char* name) {
if (!strcmp(name, XW_INTERNAL_SYNC_MESSAGING_INTERFACE_1)) {
static const XW_Internal_SyncMessagingInterface syncMessagingInterface1 = {
- [](XW_Extension extension, XW_HandleSyncMessageCallback handle_sync_msg) {
- },
- [](XW_Instance instance, const char* reply){
- }
- };
+ [](XW_Extension extension,
+ XW_HandleSyncMessageCallback handle_sync_msg) {},
+ [](XW_Instance instance, const char* reply) {},
+ [](XW_Instance instance, const char* reply, int size) {}};
return &syncMessagingInterface1;
}
diff --git a/src/common/XW_Extension_SyncMessage.h b/src/common/XW_Extension_SyncMessage.h
index c2d17b30..283d8c25 100644
--- a/src/common/XW_Extension_SyncMessage.h
+++ b/src/common/XW_Extension_SyncMessage.h
@@ -33,6 +33,7 @@ typedef void (*XW_HandleSyncMessageCallback)(XW_Instance instance, const char* m
struct XW_Internal_SyncMessagingInterface_1 {
void (*Register)(XW_Extension extension, XW_HandleSyncMessageCallback handle_sync_message);
void (*SetSyncReply)(XW_Instance instance, const char* reply);
+ void (*SetSyncBinaryReply)(XW_Instance instance, const std::vector& reply);
};
typedef struct XW_Internal_SyncMessagingInterface_1 XW_Internal_SyncMessagingInterface;
diff --git a/src/common/extension.cc b/src/common/extension.cc
index 94d17a20..4c444fee 100644
--- a/src/common/extension.cc
+++ b/src/common/extension.cc
@@ -325,6 +325,17 @@ void Instance::SendSyncReply(const char* reply) {
g_sync_messaging->SetSyncReply(xw_instance_, reply);
}
+void Instance::SendSyncBinaryReply(const std::vector& reply) {
+ ScopeLogger();
+ if (!xw_instance_) {
+ LoggerE(
+ "Ignoring SendSyncBinaryReply() in the constructor or after the "
+ "instance was destroyed.");
+ return;
+ }
+ g_sync_messaging->SetSyncBinaryReply(xw_instance_, reply);
+}
+
ParsedInstance::ParsedInstance() {
ScopeLogger();
}
@@ -343,6 +354,12 @@ void ParsedInstance::RegisterSyncHandler(const std::string& name, const NativeHa
handler_map_.insert(std::make_pair("#SYNC#" + name, func));
}
+void ParsedInstance::RegisterSyncHandlerWithBinaryAnswer(
+ const std::string& name, const NativeHandlerWithBinaryAnswer& func) {
+ ScopeLogger();
+ handler_with_binary_anser_map_.insert(std::make_pair("#SYNC#" + name, func));
+}
+
void ParsedInstance::RegisterBinaryHandler(const BinaryNativeHandler& func) {
ScopeLogger();
binary_handler_vec_.push_back(func);
@@ -416,14 +433,9 @@ void ParsedInstance::HandleMessage(const char* msg, bool is_sync) {
}
std::string cmd = (is_sync ? "#SYNC#" : "") + value.get("cmd").to_str();
-
- auto it = handler_map_.find(cmd);
- if (handler_map_.end() == it) {
- LoggerE("Unknown command: %s", cmd.c_str());
- throw UnknownException("Unknown command.");
- }
-
- NativeHandler func = it->second;
+ const bool is_binary_answer = value.contains("__binaryAnswer") &&
+ value.get("__binaryAnswer").is() &&
+ value.get("__binaryAnswer").get();
// check for args in JSON message
const picojson::value& args = value.get("args");
@@ -431,10 +443,30 @@ void ParsedInstance::HandleMessage(const char* msg, bool is_sync) {
throw InvalidValuesException("No \"args\" field in message");
}
- picojson::value result = picojson::value(picojson::object());
- func(args, result.get());
-
- if (is_sync) SendSyncReply(result.serialize().c_str());
+ if (is_binary_answer) {
+ auto it = handler_with_binary_anser_map_.find(cmd);
+ if (handler_with_binary_anser_map_.end() == it) {
+ LoggerE("Unknown binary answer command: %s", cmd.c_str());
+ throw UnknownException("Unknown command.");
+ }
+
+ NativeHandlerWithBinaryAnswer func = it->second;
+ std::vector vec;
+ func(args, &vec);
+ if (is_sync) SendSyncBinaryReply(vec);
+ } else {
+ auto it = handler_map_.find(cmd);
+ if (handler_map_.end() == it) {
+ LoggerE("Unknown json answer command: %s", cmd.c_str());
+ throw UnknownException("Unknown command.");
+ }
+
+ NativeHandler func = it->second;
+ picojson::value result = picojson::value(picojson::object());
+ func(args, result.get());
+
+ if (is_sync) SendSyncReply(result.serialize().c_str());
+ }
} catch (const PlatformException& e) {
return HandleException(e);
} catch (const PlatformException* e) {
diff --git a/src/common/extension.h b/src/common/extension.h
index d4594921..7e6cf3f1 100644
--- a/src/common/extension.h
+++ b/src/common/extension.h
@@ -104,6 +104,7 @@ class Instance {
void PostMessage(const char* msg);
void PostMessage(const char* msg, size_t size);
void SendSyncReply(const char* reply);
+ void SendSyncBinaryReply(const std::vector& reply);
virtual void Initialize() {
}
@@ -128,6 +129,8 @@ class Instance {
};
typedef std::function NativeHandler;
+typedef std::function*)>
+ NativeHandlerWithBinaryAnswer;
typedef std::function BinaryNativeHandler;
class ParsedInstance : public Instance {
@@ -138,6 +141,8 @@ class ParsedInstance : public Instance {
protected:
void RegisterHandler(const std::string& name, const NativeHandler& func);
void RegisterSyncHandler(const std::string& name, const NativeHandler& func);
+ void RegisterSyncHandlerWithBinaryAnswer(const std::string& name,
+ const NativeHandlerWithBinaryAnswer& func);
void RegisterBinaryHandler(const BinaryNativeHandler& func);
void RegisterBinarySyncHandler(const BinaryNativeHandler& func);
@@ -162,6 +167,7 @@ class ParsedInstance : public Instance {
void HandleError(const PlatformResult& error);
std::map handler_map_;
+ std::map handler_with_binary_anser_map_;
std::vector binary_handler_vec_;
};
diff --git a/src/common/logger.h b/src/common/logger.h
index e4a6f3cd..d7a319f8 100644
--- a/src/common/logger.h
+++ b/src/common/logger.h
@@ -172,6 +172,19 @@ class LogMessageVoidify {
#define LogAndReportError_X(_0, _1, _2, _3, FUNC, ...) FUNC
+#define LogAndReportErrorAsBinary_2(error, object) \
+ do { \
+ LoggerE("Reporting error."); \
+ ReportError(error, object); \
+ } while (false)
+
+#define LogAndReportErrorAsBinary_3(error, object, log) \
+ do { \
+ LoggerE log; \
+ ReportError(error, object); \
+ } while (false)
+#define LogAndReportErrorAsBinary_X(_0, _1, _2, _3, FUNC, ...) FUNC
+
// usage:
// LogAndCreateResult(const common::PlatformResult& result, picojson::object* obj, [(const char*
// log, ...)])
@@ -183,6 +196,10 @@ class LogMessageVoidify {
LogAndReportError_X(, ##__VA_ARGS__, LogAndReportError_3(__VA_ARGS__), \
LogAndReportError_2(__VA_ARGS__))
+#define LogAndReportErrorAsBinary(...) \
+ LogAndReportErrorAsBinary_X(, ##__VA_ARGS__, LogAndReportErrorAsBinary_3(__VA_ARGS__), \
+ LogAndReportErrorAsBinary_2(__VA_ARGS__))
+
// internal macros
#define LogAndCreateResult_1(error_code) \
(LoggerE("Creating PlatformResult with error"), common::PlatformResult(error_code))
diff --git a/src/common/tools.cc b/src/common/tools.cc
index 552af1a1..332a79b9 100644
--- a/src/common/tools.cc
+++ b/src/common/tools.cc
@@ -17,10 +17,10 @@
#include "common/tools.h"
#include
-#include
#include
#include
#include
+#include
#ifdef PRIVILEGE_USE_DB
#include
@@ -69,6 +69,59 @@ void ReportError(const PlatformResult& error, picojson::object* out) {
out->insert(std::make_pair("error", error.ToJSON()));
}
+void PushJSONToBinary(const picojson::value& in, std::vector* out) {
+ ScopeLogger();
+ auto out_json_data = in.serialize();
+ // As binary data used in 'out' vector after a JSON can be coded as 1, 2, 4 or
+ // 8 bytes per element, there could be an error in JS layer when trying to
+ // parse element starting from 'wrong' index depending on data length. To
+ // overcome a problem we always allign the array to end with length of bytes
+ // being a multiply of 8 to keep simplicity of implementation.
+ size_t trailing_bytes = (out_json_data.size() + 4) % 8;
+ if (trailing_bytes != 0) {
+ std::string align_bytes((8 - trailing_bytes), ' ');
+ out_json_data.insert(out_json_data.end(), align_bytes.begin(), align_bytes.end());
+ }
+
+ size_t out_json_size = out_json_data.size();
+ auto out_json_size_vec = std::vector{
+ static_cast(out_json_size >> 24), static_cast(out_json_size >> 16),
+ static_cast(out_json_size >> 8), static_cast(out_json_size)};
+ out->insert(out->end(), out_json_size_vec.begin(), out_json_size_vec.end());
+ out->insert(out->end(), out_json_data.begin(), out_json_data.end());
+}
+
+void ReportSuccessToBinary(const picojson::value& result, std::vector* out) {
+ ScopeLogger();
+ picojson::value wrapped_result{picojson::object{}};
+ picojson::object& obj = wrapped_result.get();
+
+ ReportSuccess(result, obj);
+ PushJSONToBinary(wrapped_result, out);
+}
+
+void ReportErrorToBinary(const PlatformResult& error, std::vector* out) {
+ LoggerE("PlatformResult: %d, message: %s", static_cast(error.error_code()),
+ error.message().c_str());
+ picojson::value wrapped_result{picojson::object{}};
+ picojson::object& obj = wrapped_result.get();
+
+ ReportError(error, &obj);
+ PushJSONToBinary(wrapped_result, out);
+}
+
+void ReportDataToBinary(const std::vector& in, std::vector* out) {
+ ScopeLogger();
+ unsigned long long out_data_size = in.size();
+ auto out_data_size_vec = std::vector{
+ static_cast(out_data_size >> 56), static_cast(out_data_size >> 48),
+ static_cast(out_data_size >> 40), static_cast(out_data_size >> 32),
+ static_cast(out_data_size >> 24), static_cast(out_data_size >> 16),
+ static_cast(out_data_size >> 8), static_cast(out_data_size)};
+ out->insert(out->end(), out_data_size_vec.begin(), out_data_size_vec.end());
+ out->insert(out->end(), in.begin(), in.end());
+}
+
namespace {
#ifdef PRIVILEGE_USE_DB
@@ -280,7 +333,8 @@ PlatformResult CheckAccess(const std::vector& privileges) {
/*
* This lock is crucial to avoid concurrent access of static variables in this function and
* in AccessControlImpl class.
- * Concurrent access is likely in web service apps. If multiple are running, each of them is executed in
+ * Concurrent access is likely in web service apps. If multiple are running, each of them is
+ * executed in
* a thread of the same process.
*/
static std::mutex check_access_mutex;
diff --git a/src/common/tools.h b/src/common/tools.h
index 84df7276..28d37812 100644
--- a/src/common/tools.h
+++ b/src/common/tools.h
@@ -34,6 +34,10 @@ void ReportError(picojson::object& out);
void ReportError(const PlatformException& ex, picojson::object& out);
void ReportError(const PlatformResult& error, picojson::object* out);
+void ReportSuccessToBinary(const picojson::value& result, std::vector* out);
+void ReportErrorToBinary(const PlatformResult& error, std::vector* out);
+void ReportDataToBinary(const std::vector& in, std::vector* out);
+
common::PlatformResult CheckAccess(const std::string& privilege);
common::PlatformResult CheckAccess(const std::vector& privileges);
common::PlatformResult CheckStorageAccess(const std::string& path);
diff --git a/src/ml/js/ml_common.js b/src/ml/js/ml_common.js
index ffe4e7b9..344ba00f 100755
--- a/src/ml/js/ml_common.js
+++ b/src/ml/js/ml_common.js
@@ -186,20 +186,28 @@ TensorsData.prototype.getTensorRawData = function() {
size: args.size ? args.size : []
};
- var result = native_.callSync('MLTensorsDataGetTensorRawData', callArgs);
+ var result = native_.callSyncWithBinaryAnswer(
+ 'MLTensorsDataGetTensorRawDataBinary',
+ callArgs
+ );
- if (native_.isFailure(result)) {
+ // FORMAT (decoding using a parser working sequentially on binary stream):
+ // JSON as binary
+ // data as binary
+ var parser = new privUtils_.ArrayBufferParser(result);
+ var json_result = parser.getJSON();
+ if (native_.isFailure(json_result)) {
throw native_.getErrorObjectAndValidate(
- result,
+ json_result,
TensorsInfoGettersSettersValidExceptions,
AbortError
);
}
-
- var data = privUtils_.StringToArray(result.buffer, Uint8Array);
- var ArrayType = _GetBufferTypeFromTensorType(result.type);
- var shape = result.shape;
- return new TensorRawData(new ArrayType(data.buffer), data.byteLength, shape);
+ var result_obj = native_.getResultObject(json_result);
+ var ArrayType = _GetBufferTypeFromTensorType(result_obj.type);
+ var data = parser.getData(ArrayType);
+ var shape = result_obj.shape;
+ return new TensorRawData(data, data.byteLength, shape);
};
var TensorsDataSetTensorRawDataExceptions = [
@@ -234,15 +242,6 @@ function ValidateBufferForTensorsData(tensorsData, index, buffer) {
return ret;
}
-function stringToUint8Array(str) {
- // utf-8
- var buf = new Uint8Array(str.length);
- for (var i = 0, strLen = str.length; i < strLen; i++) {
- buf[i] = str.charCodeAt(i);
- }
- return buf;
-}
-
TensorsData.prototype.setTensorRawData = function() {
_CheckIfTensorsDataNotDisposed(this._id);
var argsIndex = validator_.validateArgs(arguments, [
@@ -289,7 +288,7 @@ TensorsData.prototype.setTensorRawData = function() {
location: argsLocSize.location ? argsLocSize.location : [],
size: argsLocSize.size ? argsLocSize.size : []
};
- var callArgsCoded = stringToUint8Array(JSON.stringify(callArgs));
+ var callArgsCoded = privUtils_.StringToArray(JSON.stringify(callArgs), Uint8Array);
var buffer = new Uint8Array(buffer.buffer);
diff --git a/src/ml/ml_instance.cc b/src/ml/ml_instance.cc
index 63ac32d5..d15e99c7 100644
--- a/src/ml/ml_instance.cc
+++ b/src/ml/ml_instance.cc
@@ -109,6 +109,8 @@ MlInstance::MlInstance()
#define REGISTER_METHOD(M) RegisterSyncHandler(#M, std::bind(&MlInstance::M, this, _1, _2))
#define REGISTER_BINARY_METHOD(M) RegisterBinaryHandler(std::bind(&MlInstance::M, this, _1, _2, _3))
+#define REGISTER_METHOD_WITH_BINARY_ANWSER(M) \
+ RegisterSyncHandlerWithBinaryAnswer(#M, std::bind(&MlInstance::M, this, _1, _2))
REGISTER_METHOD(MLCheckNNFWAvailability);
REGISTER_METHOD(MLTensorsInfoCountGetter);
@@ -130,6 +132,7 @@ MlInstance::MlInstance()
REGISTER_METHOD(MLTensorsDataDispose);
REGISTER_METHOD(MLTensorsDataGetTensorRawData);
+ REGISTER_METHOD_WITH_BINARY_ANWSER(MLTensorsDataGetTensorRawDataBinary);
REGISTER_METHOD(MLTensorsDataGetTensorType);
REGISTER_METHOD(MLTensorsDataSetTensorRawData);
REGISTER_BINARY_METHOD(MLTensorsDataSetTensorRawDataBinary);
@@ -594,6 +597,7 @@ void MlInstance::MLTensorsDataDispose(const picojson::value& args, picojson::obj
}
ReportSuccess(out);
}
+
void MlInstance::MLTensorsDataGetTensorRawData(const picojson::value& args, picojson::object& out) {
ScopeLogger("args: %s", args.serialize().c_str());
CHECK_ARGS(args, kTensorsDataId, double, out);
@@ -655,6 +659,81 @@ void MlInstance::MLTensorsDataGetTensorRawData(const picojson::value& args, pico
ReportSuccess(out);
}
+void MlInstance::MLTensorsDataGetTensorRawDataBinary(const picojson::value& args,
+ std::vector* out) {
+ ScopeLogger("args: %s", args.serialize().c_str());
+ // TODO handle errors to out
+ // CHECK_ARGS(args, kTensorsDataId, double, out);
+ // CHECK_ARGS(args, kIndex, double, out);
+ // CHECK_ARGS(args, kLocation, picojson::array, out);
+ // CHECK_ARGS(args, kSize, picojson::array, out);
+
+ int tensor_data_id = static_cast(args.get(kTensorsDataId).get());
+ int index = static_cast(args.get(kIndex).get());
+
+ TensorsData* tensors_data = GetTensorsDataManager().GetTensorsData(tensor_data_id);
+ if (nullptr == tensors_data) {
+ LoggerE("Could not find TensorsData handle with given id: %d", tensor_data_id);
+ tools::ReportErrorToBinary(PlatformResult(ErrorCode::ABORT_ERR, "Internal TensorsData error"),
+ out);
+ return;
+ }
+
+ unsigned int location[ML_TENSOR_RANK_LIMIT] = {};
+ PlatformResult result =
+ util::GetLocationFromJsonArray(args.get(kLocation).get(), location);
+ if (!result) {
+ LoggerE("Reporting error.");
+ tools::ReportErrorToBinary(result, out);
+ return;
+ }
+
+ unsigned int dimensions[ML_TENSOR_RANK_LIMIT] = {};
+ result = tensors_data->GetTensorsInfo()->NativeGetTensorDimensions(index, dimensions);
+ if (!result) {
+ LoggerE("Reporting error.");
+ tools::ReportErrorToBinary(result, out);
+ return;
+ }
+
+ unsigned int size[ML_TENSOR_RANK_LIMIT] = {};
+ result = util::GetSizeFromJsonArray(args.get(kSize).get(), location, dimensions,
+ size);
+ if (!result) {
+ LoggerE("Reporting error.");
+ tools::ReportErrorToBinary(result, out);
+ return;
+ }
+
+ TensorRawData raw_data;
+ result = tensors_data->GetTensorRawData(index, location, size, &raw_data);
+ if (!result) {
+ LoggerE("Reporting error.");
+ tools::ReportErrorToBinary(result, out);
+ return;
+ }
+
+ picojson::value result_json = picojson::value(picojson::object());
+ auto& out_json = result_json.get();
+
+ out_json[kType] = picojson::value(raw_data.type_str);
+ picojson::array shape = picojson::array{};
+ for (int i = 0; i < ML_TENSOR_RANK_LIMIT; i++) {
+ shape.push_back(picojson::value{static_cast(raw_data.shape[i])});
+ }
+ out_json[kShape] = picojson::value{shape};
+
+ std::vector out_data{raw_data.data, raw_data.data + raw_data.size_in_bytes};
+
+ // FORMAT:
+ // 4 byte === JSON lenght (N)
+ // N bytest === JSON data
+ tools::ReportSuccessToBinary(result_json, out);
+ // 4 byte === buffer length (M)
+ // M bytes === buffer data
+ tools::ReportDataToBinary(out_data, out);
+}
+
void MlInstance::MLTensorsDataGetTensorType(const picojson::value& args, picojson::object& out) {
ScopeLogger("args: %s", args.serialize().c_str());
CHECK_ARGS(args, kTensorsDataId, double, out);
diff --git a/src/ml/ml_instance.h b/src/ml/ml_instance.h
index d4984e20..5ea36d5d 100644
--- a/src/ml/ml_instance.h
+++ b/src/ml/ml_instance.h
@@ -57,6 +57,7 @@ class MlInstance : public common::ParsedInstance {
void MLTensorsDataDispose(const picojson::value& args, picojson::object& out);
void MLTensorsDataGetTensorRawData(const picojson::value& args, picojson::object& out);
+ void MLTensorsDataGetTensorRawDataBinary(const picojson::value& args, std::vector* vec);
void MLTensorsDataGetTensorType(const picojson::value& args, picojson::object& out);
void MLTensorsDataSetTensorRawData(const picojson::value& args, picojson::object& out);
void MLTensorsDataSetTensorRawDataBinary(const char* data, size_t size, picojson::object& out);
diff --git a/src/tool/desc_gentool.cc b/src/tool/desc_gentool.cc
index 86d31b0d..3b58e003 100644
--- a/src/tool/desc_gentool.cc
+++ b/src/tool/desc_gentool.cc
@@ -112,8 +112,10 @@ const void* get_interface(const char* name) {
if (!strcmp(name, XW_INTERNAL_SYNC_MESSAGING_INTERFACE_1)) {
static const XW_Internal_SyncMessagingInterface syncMessagingInterface1 = {
- [](XW_Extension extension, XW_HandleSyncMessageCallback handle_sync_msg) {},
- [](XW_Instance instance, const char* reply) {}};
+ [](XW_Extension extension,
+ XW_HandleSyncMessageCallback handle_sync_msg) {},
+ [](XW_Instance instance, const char* reply) {},
+ [](XW_Instance instance, const std::vector& reply) {}};
return &syncMessagingInterface1;
}
diff --git a/src/utils/utils_api.js b/src/utils/utils_api.js
index e5dde70c..5f7b478f 100644
--- a/src/utils/utils_api.js
+++ b/src/utils/utils_api.js
@@ -1201,6 +1201,8 @@ var NativeManager = function(extension) {
!extension.internal ||
!_type.isFunction(extension.postMessage) ||
!_type.isFunction(extension.internal.sendSyncMessage) ||
+ !_type.isFunction(extension.internal.sendSyncMessageWithBinaryReply) ||
+ !_type.isFunction(extension.internal.sendSyncMessageWithStringReply) ||
!_type.isFunction(extension.sendRuntimeMessage) ||
!_type.isFunction(extension.sendRuntimeAsyncMessage) ||
!_type.isFunction(extension.sendRuntimeSyncMessage) ||
@@ -1325,6 +1327,21 @@ NativeManager.prototype.callSync = function(cmd, args) {
return JSON_.parse(response);
};
+NativeManager.prototype.callSyncWithBinaryAnswer = function (cmd, args) {
+ var request = JSON_.stringify({
+ __binaryAnswer: true,
+ cmd: cmd,
+ args: args || {}
+ });
+
+ var response = this.extension.internal.sendSyncMessageWithBinaryReply(request);
+ if (response === undefined) {
+ /* C++ extension didn't set sync binary response using Instance::SendSyncBinaryReply */
+ throw new WebAPIException(WebAPIException.ABORT_ERR, 'Internal error');
+ }
+ return response;
+};
+
NativeManager.prototype.callSyncBinaryWithJSONAnswer = function(uint8array_data) {
// method id should be coded as first byte, refer to:
// extension.cc - ParsedInstance:: HandleBinaryMessage
@@ -1643,6 +1660,66 @@ scope = scope || {};
scope.WebAPIException = WebAPIException;
scope.WebAPIError = WebAPIException;
+// ArrayBufferParser is used for parsing the binary data returned from C++ as ArrayBuffer.
+// It expects contents like:
+// - JSON coded as utf-8 format (1 byte per element)
+// - binary data of any ArrayBuffer (M bytes per element)
+// WARNING: for convienence of parsing data in JS layer, we always provide
+// JSON part (size + coded JSON) which length is a multiplication of 8 to
+// handle 1, 2, 4 and 8 bytes data smoothly.
+// Refer to common::tools::ReportSuccessToBinary, ReportErrorToBinary and
+// ReportDataToBinary methods
+function ArrayBufferParser(array) {
+ this.idx_ = 0;
+ this.typedArray_ = new Uint8Array(array);
+}
+
+ArrayBufferParser.prototype.getJSON = function () {
+ if (this.idx_ >= this.typedArray_.length) {
+ console.log('No more data');
+ return;
+ }
+ // expecting 4 bytes size (N) starting from startIdx
+ // N bytes of data
+
+ var N =
+ (this.typedArray_[this.idx_] << 24) +
+ (this.typedArray_[this.idx_ + 1] << 16) +
+ (this.typedArray_[this.idx_ + 2] << 8) +
+ this.typedArray_[this.idx_ + 3];
+
+ var subArray = this.typedArray_.slice(this.idx_ + 4, this.idx_ + 4 + N);
+ var resultJson = JSON.parse(Utils.prototype.ArrayToString(subArray));
+ this.idx_ += N + 4;
+ return resultJson;
+};
+
+ArrayBufferParser.prototype.getData = function (ArrayType) {
+ if (this.idx_ >= this.typedArray_.length) {
+ console.log('No more data');
+ return;
+ }
+ // Expecting 8 bytes size (N) starting from startIdx
+ // N bytes of data. Refer to common::tools::PushJSONToBinary comment.
+ var N =
+ (this.typedArray_[this.idx_] << 56) +
+ (this.typedArray_[this.idx_ + 1] << 48) +
+ (this.typedArray_[this.idx_ + 2] << 40) +
+ (this.typedArray_[this.idx_ + 3] << 32) +
+ (this.typedArray_[this.idx_ + 4] << 24) +
+ (this.typedArray_[this.idx_ + 5] << 16) +
+ (this.typedArray_[this.idx_ + 6] << 8) +
+ this.typedArray_[this.idx_ + 7];
+
+ // As ArrayType constructor expects the size in elements (not bytes),
+ // N need to be adjusted
+ N /= ArrayType.BYTES_PER_ELEMENT;
+ // getting N elements starting from idx_ + 8
+ var subArray = new ArrayType(this.typedArray_.buffer, this.idx_ + 8, N);
+ this.idx_ += N + 8;
+ return subArray;
+};
+
Utils.prototype.dateConverter = _dateConverter;
Utils.prototype.type = _type;
Utils.prototype.converter = _converter;
@@ -1650,6 +1727,7 @@ Utils.prototype.validator = _validator;
Utils.prototype.NativeManager = NativeManager;
Utils.prototype.CommonListenerManager = CommonListenerManager;
Utils.prototype.exportModuleIfFeatureSupported = exportModuleIfFeatureSupported;
+Utils.prototype.ArrayBufferParser = ArrayBufferParser;
var native_ = new NativeManager(extension);