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);