diff --git a/packages/react-native-executorch/cpp/core/conversions.h b/packages/react-native-executorch/cpp/core/conversions.h index e42ae08764..50262c3e73 100644 --- a/packages/react-native-executorch/cpp/core/conversions.h +++ b/packages/react-native-executorch/cpp/core/conversions.h @@ -150,6 +150,7 @@ std::vector fromJsiTypedArray(jsi::Runtime &rt, const std::string &ctx, const return vec; } +/** Helper constant for static_assert in dependent template contexts. */ template inline constexpr bool kAlwaysFalse = false; diff --git a/packages/react-native-executorch/cpp/core/dtype.cpp b/packages/react-native-executorch/cpp/core/dtype.cpp index b183daf0d6..8c57332de8 100644 --- a/packages/react-native-executorch/cpp/core/dtype.cpp +++ b/packages/react-native-executorch/cpp/core/dtype.cpp @@ -2,7 +2,7 @@ #include namespace rnexecutorch::core::types { -DType parseDType(const std::string &s) { +DType dtypeFromString(const std::string &s) { if (s == "uint8") { return DType::uint8; } @@ -18,7 +18,7 @@ DType parseDType(const std::string &s) { throw std::invalid_argument("Unsupported dtype: '" + s + "'. Expected 'uint8', 'int32', 'int64', or 'float32'"); } -std::string toString(DType dtype) { +std::string dtypeToString(DType dtype) { switch (dtype) { case DType::uint8: return "uint8"; @@ -31,7 +31,7 @@ std::string toString(DType dtype) { } } -executorch::aten::ScalarType toScalarType(DType dtype) { +executorch::aten::ScalarType dtypeToScalarType(DType dtype) { switch (dtype) { case DType::uint8: return executorch::aten::ScalarType::Byte; @@ -44,7 +44,7 @@ executorch::aten::ScalarType toScalarType(DType dtype) { } } -DType fromScalarType(executorch::aten::ScalarType st) { +DType dtypeFromScalarType(executorch::aten::ScalarType st) { switch (st) { case executorch::aten::ScalarType::Byte: return DType::uint8; diff --git a/packages/react-native-executorch/cpp/core/dtype.h b/packages/react-native-executorch/cpp/core/dtype.h index fafa0a8429..d9fdef21ff 100644 --- a/packages/react-native-executorch/cpp/core/dtype.h +++ b/packages/react-native-executorch/cpp/core/dtype.h @@ -5,6 +5,10 @@ #include namespace rnexecutorch::core::types { + +/** + * Supported tensor data types across the native runtime and JavaScript interface. + */ enum class DType { uint8, int32, @@ -12,12 +16,46 @@ enum class DType { float32 }; -DType parseDType(const std::string &s); -std::string toString(DType dtype); +/** + * Parses a string representation into a DType enum value. + * + * @param s The string name of the data type (e.g. "uint8", "int32", "int64", "float32"). + * @return The corresponding DType enum value. + * @throws std::invalid_argument If the string does not match any known DType. + */ +DType dtypeFromString(const std::string &s); + +/** + * Converts a DType enum value to its string representation. + * + * @param dtype The DType enum value to convert. + * @return The string representation of the data type. + */ +std::string dtypeToString(DType dtype); + +/** + * Converts a DType enum value to the corresponding ExecuTorch ScalarType. + * + * @param dtype The DType enum value to convert. + * @return The corresponding ExecuTorch ScalarType. + */ +executorch::aten::ScalarType dtypeToScalarType(DType dtype); -executorch::aten::ScalarType toScalarType(DType dtype); -DType fromScalarType(executorch::aten::ScalarType st); +/** + * Converts an ExecuTorch ScalarType to the corresponding DType enum value. + * + * @param st The ExecuTorch ScalarType to convert. + * @return The corresponding DType enum value. + * @throws std::invalid_argument If the ScalarType is not supported. + */ +DType dtypeFromScalarType(executorch::aten::ScalarType st); +/** + * Returns the byte size of a single element for the specified DType. + * + * @param dtype The DType enum value. + * @return The size in bytes of a single element of that data type. + */ size_t elementSize(DType dtype); } // namespace rnexecutorch::core::types diff --git a/packages/react-native-executorch/cpp/core/model.cpp b/packages/react-native-executorch/cpp/core/model.cpp index 2ff28cd2c3..642ca68727 100644 --- a/packages/react-native-executorch/cpp/core/model.cpp +++ b/packages/react-native-executorch/cpp/core/model.cpp @@ -229,7 +229,7 @@ jsi::Value ModelHostObject::get(jsi::Runtime &rt, const jsi::PropNameID &name) { auto ctx = std::format("execute: outputTensors[{}]", tensorOutputIdx); auto val = outputTensorsArray.getValueAtIndex(rt, tensorOutputIdx); - auto dtype = types::fromScalarType(output.toTensor().dtype()); + auto dtype = types::dtypeFromScalarType(output.toTensor().dtype()); auto shape = output.toTensor().sizes(); auto tensorHostObject = tensor::fromJs(rt, ctx, val, dtype, shape); diff --git a/packages/react-native-executorch/cpp/core/schema.cpp b/packages/react-native-executorch/cpp/core/schema.cpp index e26e6dfd5e..c25bc74829 100644 --- a/packages/react-native-executorch/cpp/core/schema.cpp +++ b/packages/react-native-executorch/cpp/core/schema.cpp @@ -116,7 +116,7 @@ void to_json(json &j, const ConcreteDim &d) { void from_json(const json &j, ParamSpec &p) { p.tag = j.at("kind").get(); if (p.tag == Tag::Tensor) { - p.dtype = types::parseDType(j.at("dtype").get()); + p.dtype = types::dtypeFromString(j.at("dtype").get()); p.shape = j.at("shape").get>(); } } @@ -125,7 +125,7 @@ void to_json(json &j, const ParamSpec &p) { if (p.tag == Tag::Tensor) { // DType is (de)serialized via its string helpers — a JSON macro for it // would have to live in namespace `types` for ADL to find it. - j = json::object({{"kind", "Tensor"}, {"dtype", types::toString(p.dtype)}, {"shape", p.shape}}); + j = json::object({{"kind", "Tensor"}, {"dtype", types::dtypeToString(p.dtype)}, {"shape", p.shape}}); } else { j = json::object({{"kind", p.tag}}); } @@ -236,7 +236,7 @@ ParamSpec tensorMetaToParamSpec(const executorch::runtime::TensorInfo &tensorMet const auto sizes = tensorMeta.sizes(); return ParamSpec{ .tag = Tag::Tensor, - .dtype = types::fromScalarType(tensorMeta.scalar_type()), + .dtype = types::dtypeFromScalarType(tensorMeta.scalar_type()), .shape = std::vector(sizes.begin(), sizes.end()), }; } @@ -343,10 +343,10 @@ void validateSpecDimDomains(const MethodSpec &spec, const std::string &ctx) { void validateTensorParam(const ParamSpec ¶m, const executorch::runtime::TensorInfo &tensorMeta, const std::string &ctx) { - auto metaDtype = types::fromScalarType(tensorMeta.scalar_type()); + auto metaDtype = types::dtypeFromScalarType(tensorMeta.scalar_type()); if (param.dtype != metaDtype) { throw std::runtime_error(std::format("{}: dtype mismatch (spec type '{}' != compiled metadata type '{}')", - ctx, types::toString(param.dtype), types::toString(metaDtype))); + ctx, types::dtypeToString(param.dtype), types::dtypeToString(metaDtype))); } auto metaShape = tensorMeta.sizes(); diff --git a/packages/react-native-executorch/cpp/core/tensor.cpp b/packages/react-native-executorch/cpp/core/tensor.cpp index b382d5269b..8973e5475d 100644 --- a/packages/react-native-executorch/cpp/core/tensor.cpp +++ b/packages/react-native-executorch/cpp/core/tensor.cpp @@ -29,7 +29,7 @@ TensorHostObject::TensorHostObject(const std::vector &shape, DType size_(numel_ * types::elementSize(dtype)) { // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,modernize-avoid-c-arrays): owning runtime-sized byte buffer data_ = std::make_unique(size_); - tensor_ = executorch::extension::from_blob(data_.get(), shape_, types::toScalarType(dtype)); + tensor_ = executorch::extension::from_blob(data_.get(), shape_, types::dtypeToScalarType(dtype)); } jsi::Value TensorHostObject::get(jsi::Runtime &rt, const jsi::PropNameID &name) { @@ -40,7 +40,7 @@ jsi::Value TensorHostObject::get(jsi::Runtime &rt, const jsi::PropNameID &name) } if (nameStr == "dtype") { - return jsi::String::createFromUtf8(rt, types::toString(dtype_)); + return jsi::String::createFromUtf8(rt, types::dtypeToString(dtype_)); } if (nameStr == "numel") { @@ -253,7 +253,7 @@ void install_createTensor(jsi::Runtime &rt, jsi::Object &module) { } try { - const auto dtype = types::parseDType(conversions::asType(rt, "createTensor: dtype", args[1])); + const auto dtype = types::dtypeFromString(conversions::asType(rt, "createTensor: dtype", args[1])); return jsi::Object::createFromHostObject(rt, std::make_shared(shape, dtype)); } catch (const std::exception &e) { throw jsi::JSError(rt, std::format("createTensor: Error creating tensor: {}", e.what())); diff --git a/packages/react-native-executorch/cpp/core/tensor.h b/packages/react-native-executorch/cpp/core/tensor.h index b679e767d9..541606fc3d 100644 --- a/packages/react-native-executorch/cpp/core/tensor.h +++ b/packages/react-native-executorch/cpp/core/tensor.h @@ -26,17 +26,31 @@ namespace types = rnexecutorch::core::types; class TensorHostObject : public jsi::HostObject, public std::enable_shared_from_this { public: + /** Data type of the tensor elements. */ const types::DType dtype_; + /** Dimensions (shape) of the tensor. */ const std::vector shape_; + /** Total number of elements contained in the tensor. */ const size_t numel_; + /** Total memory size of the tensor data buffer in bytes. */ const size_t size_; - // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,modernize-avoid-c-arrays): owning runtime-sized byte buffer - std::unique_ptr data_; + /** Owning byte buffer holding the raw tensor data. */ + std::unique_ptr data_; // NOLINT(cppcoreguidelines-avoid-c-arrays,modernize-avoid-c-arrays): owning runtime-sized byte buffer + /** ExecuTorch TensorPtr instance wrapping the data buffer. */ executorch::extension::TensorPtr tensor_; + /** Shared mutex guarding concurrent read/write access to the tensor data. */ std::shared_mutex mutex_; + /** + * Constructs a TensorHostObject with the specified shape and data type. + * + * Allocates and zero-initializes the underlying memory buffer for the tensor data. + * + * @param shape The dimensions of the tensor. + * @param dtype The data type of the tensor elements. + */ TensorHostObject(const std::vector &shape, types::DType dtype); jsi::Value get(jsi::Runtime &rt, const jsi::PropNameID &name) override; diff --git a/packages/react-native-executorch/cpp/core/tensor_helpers.cpp b/packages/react-native-executorch/cpp/core/tensor_helpers.cpp index de77106ae2..861b6de1b0 100644 --- a/packages/react-native-executorch/cpp/core/tensor_helpers.cpp +++ b/packages/react-native-executorch/cpp/core/tensor_helpers.cpp @@ -99,7 +99,8 @@ fromJs(jsi::Runtime &rt, const std::string &ctx, const jsi::Value &value, const auto &shape = tensor->shape_; if (expectedDtype && dtype != *expectedDtype) { - throw jsi::JSError(rt, std::format("{} must be of type {} (got {})", ctx, types::toString(*expectedDtype), types::toString(dtype))); + throw jsi::JSError(rt, std::format("{} must be of type {} (got {})", + ctx, types::dtypeToString(*expectedDtype), types::dtypeToString(dtype))); } if (!expectedShape) { diff --git a/packages/react-native-executorch/cpp/extensions/cv/utils.h b/packages/react-native-executorch/cpp/extensions/cv/utils.h index 710124b1d9..132663a0a3 100644 --- a/packages/react-native-executorch/cpp/extensions/cv/utils.h +++ b/packages/react-native-executorch/cpp/extensions/cv/utils.h @@ -6,6 +6,13 @@ namespace rnexecutorch::extensions::cv { +/** + * Converts an ExecuTorch DType enum value to the corresponding OpenCV matrix depth constant. + * + * @param dtype The input tensor data type. + * @return The corresponding OpenCV depth constant (e.g. CV_8U, CV_32S, CV_32F). + * @throws std::invalid_argument If the data type is not supported by OpenCV depth representation. + */ inline int dtypeToCvDepth(rnexecutorch::core::types::DType dtype) { switch (dtype) { case rnexecutorch::core::types::DType::uint8: diff --git a/packages/react-native-executorch/cpp/extensions/nlp/tokenizer.h b/packages/react-native-executorch/cpp/extensions/nlp/tokenizer.h index 77a4b9d638..3e05e6f47a 100644 --- a/packages/react-native-executorch/cpp/extensions/nlp/tokenizer.h +++ b/packages/react-native-executorch/cpp/extensions/nlp/tokenizer.h @@ -11,21 +11,44 @@ #include namespace rnexecutorch::extensions::nlp::tokenizer { + +/** + * JSI HostObject wrapping a HuggingFace Tokenizer instance (`tokenizers::HFTokenizer`). + * + * Exposes methods to JavaScript for encoding text to token IDs, decoding token IDs + * to text, and managing tokenizer resources. + */ class TokenizerHostObject : public facebook::jsi::HostObject, public std::enable_shared_from_this { public: - // Loads the tokenizer from `tokenizerPath`; throws std::runtime_error on failure. + /** + * Constructs a TokenizerHostObject by loading a HuggingFace tokenizer configuration file. + * + * @param tokenizerPath File system path to the tokenizer configuration file. + * @throws std::runtime_error If loading the tokenizer fails. + */ explicit TokenizerHostObject(std::string tokenizerPath); facebook::jsi::Value get(facebook::jsi::Runtime &rt, const facebook::jsi::PropNameID &name) override; std::vector getPropertyNames(facebook::jsi::Runtime &rt) override; private: + /** + * Tries to acquire a unique lock on the tokenizer's mutex. + * Throws a facebook::jsi::JSError with contextual error info if the lock cannot be acquired. + * + * @param rt The JSI runtime instance. + * @param context Context description used to generate helpful error messages. + * @return A unique lock protecting the tokenizer. + */ [[nodiscard]] std::unique_lock tryLockUnique(facebook::jsi::Runtime &rt, std::string_view context); + /** File path to the HuggingFace tokenizer JSON configuration file. */ std::string tokenizerPath_; + /** Owning pointer to the underlying HuggingFace tokenizer instance. */ std::unique_ptr tokenizer_; + /** Mutex guarding concurrent access to the tokenizer. */ std::mutex mutex_; };