Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/react-native-executorch/cpp/core/conversions.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ std::vector<T> fromJsiTypedArray(jsi::Runtime &rt, const std::string &ctx, const
return vec;
}

/** Helper constant for static_assert in dependent template contexts. */
template <typename>
inline constexpr bool kAlwaysFalse = false;

Expand Down
8 changes: 4 additions & 4 deletions packages/react-native-executorch/cpp/core/dtype.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#include <stdexcept>

namespace rnexecutorch::core::types {
DType parseDType(const std::string &s) {
DType dtypeFromString(const std::string &s) {
if (s == "uint8") {
return DType::uint8;
}
Expand All @@ -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";
Expand All @@ -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;
Expand All @@ -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;
Expand Down
46 changes: 42 additions & 4 deletions packages/react-native-executorch/cpp/core/dtype.h
Comment thread
barhanc marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,57 @@
#include <string>

namespace rnexecutorch::core::types {

/**
* Supported tensor data types across the native runtime and JavaScript interface.
*/
enum class DType {
uint8,
int32,
int64,
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
2 changes: 1 addition & 1 deletion packages/react-native-executorch/cpp/core/model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
10 changes: 5 additions & 5 deletions packages/react-native-executorch/cpp/core/schema.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<Tag>();
if (p.tag == Tag::Tensor) {
p.dtype = types::parseDType(j.at("dtype").get<std::string>());
p.dtype = types::dtypeFromString(j.at("dtype").get<std::string>());
p.shape = j.at("shape").get<std::vector<ConcreteDim>>();
}
}
Expand All @@ -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}});
}
Expand Down Expand Up @@ -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<ConcreteDim>(sizes.begin(), sizes.end()),
};
}
Expand Down Expand Up @@ -343,10 +343,10 @@ void validateSpecDimDomains(const MethodSpec &spec, const std::string &ctx) {
void validateTensorParam(const ParamSpec &param,
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();
Expand Down
6 changes: 3 additions & 3 deletions packages/react-native-executorch/cpp/core/tensor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ TensorHostObject::TensorHostObject(const std::vector<std::int32_t> &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<std::uint8_t[]>(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) {
Expand All @@ -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") {
Expand Down Expand Up @@ -253,7 +253,7 @@ void install_createTensor(jsi::Runtime &rt, jsi::Object &module) {
}

try {
const auto dtype = types::parseDType(conversions::asType<std::string>(rt, "createTensor: dtype", args[1]));
const auto dtype = types::dtypeFromString(conversions::asType<std::string>(rt, "createTensor: dtype", args[1]));
return jsi::Object::createFromHostObject(rt, std::make_shared<TensorHostObject>(shape, dtype));
} catch (const std::exception &e) {
throw jsi::JSError(rt, std::format("createTensor: Error creating tensor: {}", e.what()));
Expand Down
18 changes: 16 additions & 2 deletions packages/react-native-executorch/cpp/core/tensor.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,31 @@ namespace types = rnexecutorch::core::types;
class TensorHostObject : public jsi::HostObject,
public std::enable_shared_from_this<TensorHostObject> {
public:
/** Data type of the tensor elements. */
const types::DType dtype_;
/** Dimensions (shape) of the tensor. */
const std::vector<std::int32_t> 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<std::uint8_t[]> data_;
/** Owning byte buffer holding the raw tensor data. */
std::unique_ptr<std::uint8_t[]> 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<std::int32_t> &shape, types::DType dtype);

jsi::Value get(jsi::Runtime &rt, const jsi::PropNameID &name) override;
Expand Down
3 changes: 2 additions & 1 deletion packages/react-native-executorch/cpp/core/tensor_helpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
7 changes: 7 additions & 0 deletions packages/react-native-executorch/cpp/extensions/cv/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
25 changes: 24 additions & 1 deletion packages/react-native-executorch/cpp/extensions/nlp/tokenizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,44 @@
#include <pytorch/tokenizers/hf_tokenizer.h>

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<TokenizerHostObject> {
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<facebook::jsi::PropNameID> 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<std::mutex> 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<tokenizers::HFTokenizer> tokenizer_;
/** Mutex guarding concurrent access to the tokenizer. */
std::mutex mutex_;
};

Expand Down