diff --git a/v8go.cc b/v8go.cc index b435db59..1a1f271e 100644 --- a/v8go.cc +++ b/v8go.cc @@ -852,6 +852,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 local_ctx = ctx->ptr.Get(iso); + Context::Scope context_scope(local_ctx); + + std::unique_ptr bs = ArrayBuffer::NewBackingStore( + static_cast(const_cast(v)), len, + [](void* data, size_t length, void *deleter_data) { + free(data); + }, nullptr); + + Local arbuf = ArrayBuffer::New(iso, std::move(bs)); + + m_value* val = new m_value; + val->iso = iso; + val->ctx = ctx; + val->ptr = Persistent>( + iso, Uint8Array::New(arbuf, 0, len)); + + + return tracked_value(ctx, val); +} + +uint8_t* ValueToUint8Array(ValuePtr ptr) { + LOCAL_VALUE(ptr); + MaybeLocal array = value.As(); + 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 array = value.As(); + return array.ToLocalChecked()->Length(); +} + ValuePtr NewValueNull(IsolatePtr iso) { ISOLATE_SCOPE_INTERNAL_CONTEXT(iso); m_value* val = new m_value; diff --git a/v8go.h b/v8go.h index b20daca4..16fdaf2d 100644 --- a/v8go.h +++ b/v8go.h @@ -200,6 +200,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); @@ -210,6 +211,8 @@ extern RtnValue NewValueBigIntFromWords(IsolatePtr iso_ptr, const uint64_t* words); void ValueRelease(ValuePtr ptr); 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); diff --git a/value.go b/value.go index 61d4deec..9737b7dc 100644 --- a/value.go +++ b/value.go @@ -54,6 +54,7 @@ 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 @@ -61,6 +62,7 @@ func Null(iso *Isolate) *Value { // 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 ") @@ -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)), @@ -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 { diff --git a/value_test.go b/value_test.go index d386a589..b3ba9493 100644 --- a/value_test.go +++ b/value_test.go @@ -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()