Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support []byte in Value #356

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
40 changes: 40 additions & 0 deletions v8go.cc
Expand Up @@ -830,6 +830,46 @@ RtnValue NewValueString(IsolatePtr iso, const char* v, int v_length) {
return rtn;
}

ValuePtr NewValueUint8Array(IsolatePtr iso, const uint8_t *v, int len) {
ISOLATE_SCOPE_INTERNAL_CONTEXT(iso);

Local<Context> local_ctx = ctx->ptr.Get(iso);
Context::Scope context_scope(local_ctx);

std::unique_ptr<BackingStore> bs = ArrayBuffer::NewBackingStore(
static_cast<void*>(const_cast<uint8_t*>(v)), len,
[](void* data, size_t length, void *deleter_data) {
free(data);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I can tell, this seems to be the correct deallocator to use but I couldn't find a convincing example.

}, nullptr);

Local<ArrayBuffer> arbuf = ArrayBuffer::New(iso, std::move(bs));

m_value* val = new m_value;
val->iso = iso;
val->ctx = ctx;
val->ptr = Persistent<Value, CopyablePersistentTraits<Value>>(
iso, Uint8Array::New(arbuf, 0, len));


return tracked_value(ctx, val);
}

uint8_t* ValueToUint8Array(ValuePtr ptr) {
LOCAL_VALUE(ptr);
MaybeLocal<Uint8Array> array = value.As<Uint8Array>();
int length = array.ToLocalChecked()->ByteLength();
uint8_t* bytes = new uint8_t[length];
memcpy(bytes, array.ToLocalChecked()->Buffer()->GetBackingStore()->Data(), length);
return bytes;
}

// Returns length of the array (number of elements, not number of bytes)
uint64_t ValueToArrayLength(ValuePtr ptr) {
LOCAL_VALUE(ptr);
MaybeLocal<TypedArray> array = value.As<TypedArray>();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to check if this is empty?

uint64_t ValueToArrayLength(ValuePtr ptr) {
  LOCAL_VALUE(ptr);
  MaybeLocal<TypedArray> array = value.As<TypedArray>();
  if (array.IsEmpty()) {
    return 0;
  }
  return array.ToLocalChecked()->Length();
}

Same question above in ValueToUint8Array().

return array.ToLocalChecked()->Length();
}

ValuePtr NewValueNull(IsolatePtr iso) {
ISOLATE_SCOPE_INTERNAL_CONTEXT(iso);
m_value* val = new m_value;
Expand Down
3 changes: 3 additions & 0 deletions v8go.h
Expand Up @@ -199,6 +199,7 @@ extern ValuePtr NewValueUndefined(IsolatePtr iso_ptr);
extern ValuePtr NewValueInteger(IsolatePtr iso_ptr, int32_t v);
extern ValuePtr NewValueIntegerFromUnsigned(IsolatePtr iso_ptr, uint32_t v);
extern RtnValue NewValueString(IsolatePtr iso_ptr, const char* v, int v_length);
extern ValuePtr NewValueUint8Array(IsolatePtr iso_ptr, const uint8_t* v, int len);
extern ValuePtr NewValueBoolean(IsolatePtr iso_ptr, int v);
extern ValuePtr NewValueNumber(IsolatePtr iso_ptr, double v);
extern ValuePtr NewValueBigInt(IsolatePtr iso_ptr, int64_t v);
Expand All @@ -208,6 +209,8 @@ extern RtnValue NewValueBigIntFromWords(IsolatePtr iso_ptr,
int word_count,
const uint64_t* words);
extern RtnString ValueToString(ValuePtr ptr);
extern uint8_t* ValueToUint8Array(ValuePtr ptr);
extern uint64_t ValueToArrayLength(ValuePtr ptr);
const uint32_t* ValueToArrayIndex(ValuePtr ptr);
int ValueToBoolean(ValuePtr ptr);
int32_t ValueToInt32(ValuePtr ptr);
Expand Down
12 changes: 12 additions & 0 deletions value.go
Expand Up @@ -54,13 +54,15 @@ func Null(iso *Isolate) *Value {
}

// NewValue will create a primitive value. Supported values types to create are:
//
// string -> V8::String
// int32 -> V8::Integer
// uint32 -> V8::Integer
// int64 -> V8::BigInt
// uint64 -> V8::BigInt
// bool -> V8::Boolean
// *big.Int -> V8::BigInt
// []byte -> V8::ArrayBuffer
func NewValue(iso *Isolate, val interface{}) (*Value, error) {
if iso == nil {
return nil, errors.New("v8go: failed to create new Value: Isolate cannot be <nil>")
Expand All @@ -74,6 +76,10 @@ func NewValue(iso *Isolate, val interface{}) (*Value, error) {
defer C.free(unsafe.Pointer(cstr))
rtn := C.NewValueString(iso.ptr, cstr, C.int(len(v)))
return valueResult(nil, rtn)
case []uint8:
rtnVal = &Value{
ptr: C.NewValueUint8Array(iso.ptr, (*C.uchar)(C.CBytes(v)), C.int(len(v))),
}
case int32:
rtnVal = &Value{
ptr: C.NewValueInteger(iso.ptr, C.int(v)),
Expand Down Expand Up @@ -244,6 +250,12 @@ func (v *Value) String() string {
return C.GoStringN(s.data, C.int(s.length))
}

func (v *Value) Uint8Array() []uint8 {
bytes := unsafe.Pointer(C.ValueToUint8Array(v.ptr)) // allocates copy on the heap
defer C.free(bytes)
return C.GoBytes(bytes, C.int(C.ValueToArrayLength(v.ptr)))
}

// Uint32 perform the equivalent of `Number(value)` in JS and convert the result to an
// unsigned 32-bit integer by performing the steps in https://tc39.es/ecma262/#sec-touint32.
func (v *Value) Uint32() uint32 {
Expand Down
22 changes: 22 additions & 0 deletions value_test.go
Expand Up @@ -656,6 +656,28 @@ func TestValueIsXXX(t *testing.T) {
}
}

func TestValueNewUint8Array(t *testing.T) {
t.Parallel()
ctx := v8.NewContext()
iso := ctx.Isolate()
in := []uint8{1, 2, 3, 4, 5}
if val, err := v8.NewValue(iso, in); err != nil {
t.Fatalf("Error %v", err)
} else if !val.IsUint8Array() {
t.Errorf("Val is not []uint")
} else {
out := val.Uint8Array()
if len(out) != 5 {
t.Errorf("Expected array length 5, got %d", len(out))
}
for i := 0; i < 5; i++ {
if out[i] != in[i] {
t.Errorf("Wrong byte at %d", i)
}
}
}
}

func TestValueMarshalJSON(t *testing.T) {
t.Parallel()
iso := v8.NewIsolate()
Expand Down