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;
}
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;
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();
}
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);
}
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");
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) {
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() {
}
};
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 {
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);
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_;
};
#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, ...)])
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))
#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>
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
/*
* 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;
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);
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 = [
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, [
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);
#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);
REGISTER_METHOD(MLTensorsDataDispose);
REGISTER_METHOD(MLTensorsDataGetTensorRawData);
+ REGISTER_METHOD_WITH_BINARY_ANWSER(MLTensorsDataGetTensorRawDataBinary);
REGISTER_METHOD(MLTensorsDataGetTensorType);
REGISTER_METHOD(MLTensorsDataSetTensorRawData);
REGISTER_BINARY_METHOD(MLTensorsDataSetTensorRawDataBinary);
}
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);
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);
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);
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;
}
!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) ||
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
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;
Utils.prototype.NativeManager = NativeManager;
Utils.prototype.CommonListenerManager = CommonListenerManager;
Utils.prototype.exportModuleIfFeatureSupported = exportModuleIfFeatureSupported;
+Utils.prototype.ArrayBufferParser = ArrayBufferParser;
var native_ = new NativeManager(extension);