[ML] Added binary communication in getTensorRawData() 10/263110/7
authorPiotr Kosko/Tizen API (PLT) /SRPOL/Engineer/Samsung Electronics <p.kosko@samsung.com>
Thu, 26 Aug 2021 07:53:19 +0000 (09:53 +0200)
committerPiotr Kosko/Tizen API (PLT) /SRPOL/Engineer/Samsung Electronics <p.kosko@samsung.com>
Wed, 1 Sep 2021 08:04:11 +0000 (10:04 +0200)
[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 <dh81.song@samsung.com>
Signed-off-by: Piotr Kosko/Tizen API (PLT) /SRPOL/Engineer/Samsung Electronics <p.kosko@samsung.com>
12 files changed:
doc/src/assets/webapi-plugins-devel-test/src/tool/desc_gentool.cc
src/common/XW_Extension_SyncMessage.h
src/common/extension.cc
src/common/extension.h
src/common/logger.h
src/common/tools.cc
src/common/tools.h
src/ml/js/ml_common.js
src/ml/ml_instance.cc
src/ml/ml_instance.h
src/tool/desc_gentool.cc
src/utils/utils_api.js

index 93804f9..a001736 100644 (file)
@@ -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;
   }
 
index c2d17b3..283d8c2 100644 (file)
@@ -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<uint8_t>& reply);
 };
 
 typedef struct XW_Internal_SyncMessagingInterface_1 XW_Internal_SyncMessagingInterface;
index 94d17a2..4c444fe 100644 (file)
@@ -325,6 +325,17 @@ void Instance::SendSyncReply(const char* reply) {
   g_sync_messaging->SetSyncReply(xw_instance_, reply);
 }
 
+void Instance::SendSyncBinaryReply(const std::vector<uint8_t>& 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<bool>() &&
+                                  value.get("__binaryAnswer").get<bool>();
 
     // 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<picojson::object>());
-
-    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<uint8_t> 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<picojson::object>());
+
+      if (is_sync) SendSyncReply(result.serialize().c_str());
+    }
   } catch (const PlatformException& e) {
     return HandleException(e);
   } catch (const PlatformException* e) {
index d459492..7e6cf3f 100644 (file)
@@ -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<uint8_t>& reply);
 
   virtual void Initialize() {
   }
@@ -128,6 +129,8 @@ class Instance {
 };
 
 typedef std::function<void(const picojson::value&, picojson::object&)> NativeHandler;
+typedef std::function<void(const picojson::value&, std::vector<uint8_t>*)>
+    NativeHandlerWithBinaryAnswer;
 typedef std::function<void(const char*, size_t size, picojson::object&)> 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<std::string, NativeHandler> handler_map_;
+  std::map<std::string, NativeHandlerWithBinaryAnswer> handler_with_binary_anser_map_;
   std::vector<BinaryNativeHandler> binary_handler_vec_;
 };
 
index e4a6f3c..d7a319f 100644 (file)
@@ -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))
index 552af1a..332a79b 100644 (file)
 #include "common/tools.h"
 
 #include <app_manager.h>
-#include <mutex>
 #include <pkgmgr-info.h>
 #include <privilegemgr/privilege_manager.h>
 #include <sys/stat.h>
+#include <mutex>
 
 #ifdef PRIVILEGE_USE_DB
 #include <sqlite3.h>
@@ -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<uint8_t>* 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<uint8_t>{
+      static_cast<uint8_t>(out_json_size >> 24), static_cast<uint8_t>(out_json_size >> 16),
+      static_cast<uint8_t>(out_json_size >> 8), static_cast<uint8_t>(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<uint8_t>* out) {
+  ScopeLogger();
+  picojson::value wrapped_result{picojson::object{}};
+  picojson::object& obj = wrapped_result.get<picojson::object>();
+
+  ReportSuccess(result, obj);
+  PushJSONToBinary(wrapped_result, out);
+}
+
+void ReportErrorToBinary(const PlatformResult& error, std::vector<uint8_t>* out) {
+  LoggerE("PlatformResult: %d, message: %s", static_cast<int>(error.error_code()),
+          error.message().c_str());
+  picojson::value wrapped_result{picojson::object{}};
+  picojson::object& obj = wrapped_result.get<picojson::object>();
+
+  ReportError(error, &obj);
+  PushJSONToBinary(wrapped_result, out);
+}
+
+void ReportDataToBinary(const std::vector<uint8_t>& in, std::vector<uint8_t>* out) {
+  ScopeLogger();
+  unsigned long long out_data_size = in.size();
+  auto out_data_size_vec = std::vector<uint8_t>{
+      static_cast<uint8_t>(out_data_size >> 56), static_cast<uint8_t>(out_data_size >> 48),
+      static_cast<uint8_t>(out_data_size >> 40), static_cast<uint8_t>(out_data_size >> 32),
+      static_cast<uint8_t>(out_data_size >> 24), static_cast<uint8_t>(out_data_size >> 16),
+      static_cast<uint8_t>(out_data_size >> 8),  static_cast<uint8_t>(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<std::string>& 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;
index 84df727..28d3781 100644 (file)
@@ -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<uint8_t>* out);
+void ReportErrorToBinary(const PlatformResult& error, std::vector<uint8_t>* out);
+void ReportDataToBinary(const std::vector<uint8_t>& in, std::vector<uint8_t>* out);
+
 common::PlatformResult CheckAccess(const std::string& privilege);
 common::PlatformResult CheckAccess(const std::vector<std::string>& privileges);
 common::PlatformResult CheckStorageAccess(const std::string& path);
index ffe4e7b..344ba00 100755 (executable)
@@ -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);
 
index 63ac32d..d15e99c 100644 (file)
@@ -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<uint8_t>* 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<int>(args.get(kTensorsDataId).get<double>());
+  int index = static_cast<int>(args.get(kIndex).get<double>());
+
+  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<picojson::array>(), 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<picojson::array>(), 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<picojson::object>();
+
+  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<double>(raw_data.shape[i])});
+  }
+  out_json[kShape] = picojson::value{shape};
+
+  std::vector<std::uint8_t> 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);
index d4984e2..5ea36d5 100644 (file)
@@ -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<uint8_t>* 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);
index 86d31b0..3b58e00 100644 (file)
@@ -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<uint8_t>& reply) {}};
     return &syncMessagingInterface1;
   }
 
index e5dde70..5f7b478 100644 (file)
@@ -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);