Skip to content

Commit 185c12d

Browse files
authored
feat: add interop.stringFromCString (#228)
Convert a char* into a JS string (~10x faster than doing NSString.stringWithUTF8String().toString())
1 parent d67588c commit 185c12d

File tree

3 files changed

+88
-0
lines changed

3 files changed

+88
-0
lines changed

NativeScript/runtime/Interop.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ class Interop {
134134
static void InitializeStruct(v8::Local<v8::Context> context, void* destBuffer, std::vector<StructField> fields, v8::Local<v8::Value> inititalizer, ptrdiff_t& position);
135135
static void RegisterInteropType(v8::Local<v8::Context> context, v8::Local<v8::Object> types, std::string name, PrimitiveDataWrapper* wrapper, bool autoDelete = true);
136136
static void RegisterBufferFromDataFunction(v8::Local<v8::Context> context, v8::Local<v8::Object> interop);
137+
static void RegisterStringFromCString(v8::Local<v8::Context> context, v8::Local<v8::Object> interop);
137138
static void RegisterHandleOfFunction(v8::Local<v8::Context> context, v8::Local<v8::Object> interop);
138139
static void RegisterAllocFunction(v8::Local<v8::Context> context, v8::Local<v8::Object> interop);
139140
static void RegisterFreeFunction(v8::Local<v8::Context> context, v8::Local<v8::Object> interop);

NativeScript/runtime/InteropTypes.mm

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
Pointer::Register(context, interop);
2626
FunctionReference::Register(context, interop);
2727
RegisterBufferFromDataFunction(context, interop);
28+
RegisterStringFromCString(context, interop);
2829
RegisterHandleOfFunction(context, interop);
2930
RegisterAllocFunction(context, interop);
3031
RegisterFreeFunction(context, interop);
@@ -140,6 +141,60 @@
140141
tns::Assert(success, isolate);
141142
}
142143

144+
void Interop::RegisterStringFromCString(Local<Context> context, Local<Object> interop) {
145+
Local<v8::Function> func;
146+
bool success = v8::Function::New(context, [](const FunctionCallbackInfo<Value>& info) {
147+
Isolate* isolate = info.GetIsolate();
148+
tns::Assert(info.Length() >= 1 && info[0]->IsObject(), isolate);
149+
Local<Object> arg = info[0].As<Object>();
150+
int stringLength = -1;
151+
if(info.Length() >= 2 && !info[1].IsEmpty() && !info[1]->IsNullOrUndefined()) {
152+
auto desiredLength = ToNumber(isolate, info[1]);
153+
if (desiredLength != NAN) {
154+
stringLength = desiredLength;
155+
}
156+
}
157+
tns::Assert(arg->InternalFieldCount() > 0 && arg->GetInternalField(0)->IsExternal(), isolate);
158+
159+
Local<External> ext = arg->GetInternalField(0).As<External>();
160+
BaseDataWrapper* wrapper = static_cast<BaseDataWrapper*>(ext->Value());
161+
tns::Assert(wrapper != nullptr);
162+
char* data = nullptr;
163+
switch (wrapper->Type()) {
164+
case WrapperType::Pointer:
165+
{
166+
PointerWrapper* pointerWrapper = static_cast<PointerWrapper*>(wrapper);
167+
data = static_cast<char*>(pointerWrapper->Data());
168+
}
169+
break;
170+
case WrapperType::Reference:
171+
{
172+
ReferenceWrapper* referenceWrapper = static_cast<ReferenceWrapper*>(wrapper);
173+
if (referenceWrapper->Data() != nullptr) {
174+
data = static_cast<char*>(referenceWrapper->Data());
175+
break;
176+
}
177+
auto wrappedValue = referenceWrapper->Value()->Get(isolate);
178+
auto wrappedWrapper = tns::GetValue(isolate, wrappedValue);
179+
tns::Assert(wrappedWrapper->Type() == WrapperType::Pointer);
180+
data = static_cast<char*>((static_cast<PointerWrapper*>(wrappedWrapper))->Data());
181+
}
182+
default:
183+
break;
184+
}
185+
tns::Assert(data != nullptr);
186+
187+
auto result = v8::String::NewFromUtf8(isolate, data, v8::NewStringType::kNormal, stringLength).ToLocalChecked();
188+
info.GetReturnValue().Set(result);
189+
}).ToLocal(&func);
190+
191+
Isolate* isolate = context->GetIsolate();
192+
tns::Assert(success, isolate);
193+
194+
success = interop->Set(context, tns::ToV8String(isolate, "stringFromCString"), func).FromMaybe(false);
195+
tns::Assert(success, isolate);
196+
}
197+
143198
void Interop::RegisterHandleOfFunction(Local<Context> context, Local<Object> interop) {
144199
Local<v8::Function> func;
145200
bool success = v8::Function::New(context, [](const FunctionCallbackInfo<Value>& info) {

TestRunner/app/tests/Marshalling/ReferenceTests.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,38 @@ describe(module.id, function () {
329329
expect(TNSGetOutput()).toBe(str);
330330
expect(interop.handleof(result).toNumber() == interop.handleof(ptr).toNumber());
331331
expect(NSString.stringWithUTF8String(result).toString()).toBe(str);
332+
interop.free(ptr);
333+
});
334+
335+
it("interops string from CString", function () {
336+
const str = "test";
337+
const ptr = interop.alloc((str.length + 1) * interop.sizeof(interop.types.uint8));
338+
var reference = new interop.Reference(interop.types.uint8, ptr);
339+
for (ii in str) {
340+
const i = parseInt(ii);
341+
reference[i] = str.charCodeAt(i);
342+
}
343+
reference[str.length] = 0;
344+
expect(interop.stringFromCString(ptr)).toBe(str);
345+
expect(interop.stringFromCString(reference)).toBe(str);
346+
interop.free(ptr);
347+
});
348+
349+
it("interops string from CString with fixed length", function () {
350+
const str = "te\0st";
351+
const ptr = interop.alloc((str.length + 1) * interop.sizeof(interop.types.uint8));
352+
var reference = new interop.Reference(interop.types.uint8, ptr);
353+
for (ii in str) {
354+
const i = parseInt(ii);
355+
reference[i] = str.charCodeAt(i);
356+
}
357+
reference[str.length] = 0;
358+
// no length means it will go until it finds \0
359+
expect(interop.stringFromCString(ptr)).toBe('te');
360+
expect(interop.stringFromCString(ptr, 1)).toBe('t');
361+
expect(interop.stringFromCString(ptr, str.length)).toBe(str);
362+
expect(interop.stringFromCString(reference, str.length)).toBe(str);
363+
interop.free(ptr);
332364
});
333365

334366
it("CString should be passed as its UTF8 encoding and returned as a reference to unsigned characters", function () {

0 commit comments

Comments
 (0)