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

syscall/js: request: want to convert slices from/to TypedArray directly #32402

Open
hajimehoshi opened this issue Jun 3, 2019 · 4 comments

Comments

@hajimehoshi
Copy link
Contributor

commented Jun 3, 2019

As TypedArray no longer exists as of Go 1.13, there is no way to convert slices other than []byte from/to TypedArray objects other than Uint8Array. It is technically possible to do this by using encoding/binary, but I'm worried this conversion is not efficient.

As there are Web APIs that take such TypedArray objects (e.g., AudioBuffer.copyToChannel takes a Float32Array [1]), it would be very useful if syscall/js had such conversion functions.

CC @neelance

[1] https://developer.mozilla.org/en-US/docs/Web/API/AudioBuffer/copyToChannel

@hajimehoshi hajimehoshi changed the title syscall/js: request: want to convert slices to TypedArray directly syscall/js: request: want to convert slices from/to TypedArray directly Jun 3, 2019

@dennwc

This comment has been minimized.

Copy link
Contributor

commented Jun 4, 2019

A possible workaround, for now, is to cast slices with unsafe and reflect.SliceHeader.

@hajimehoshi

This comment has been minimized.

Copy link
Contributor Author

commented Jun 16, 2019

@dennwc Thanks. Now I've succeeded to do what I wanted:

func sliceToByteSlice(s interface{}) []byte {
        switch s := s.(type) {
        case []int8:
                h := (*reflect.SliceHeader)(unsafe.Pointer(&s))
                return *(*[]byte)(unsafe.Pointer(h))
        case []int16:
                h := (*reflect.SliceHeader)(unsafe.Pointer(&s))
                h.Len *= 2
                h.Cap *= 2
                return *(*[]byte)(unsafe.Pointer(h))
        case []int32:
                h := (*reflect.SliceHeader)(unsafe.Pointer(&s))
                h.Len *= 4
                h.Cap *= 4
                return *(*[]byte)(unsafe.Pointer(h))
        case []int64:
                h := (*reflect.SliceHeader)(unsafe.Pointer(&s))
                h.Len *= 8
                h.Cap *= 8
                return *(*[]byte)(unsafe.Pointer(h))
        case []uint8:
                return s
        case []uint16:
                h := (*reflect.SliceHeader)(unsafe.Pointer(&s))
                h.Len *= 2
                h.Cap *= 2
                return *(*[]byte)(unsafe.Pointer(h))
        case []uint32:
                h := (*reflect.SliceHeader)(unsafe.Pointer(&s))
                h.Len *= 4
                h.Cap *= 4
                return *(*[]byte)(unsafe.Pointer(h))
        case []uint64:
                h := (*reflect.SliceHeader)(unsafe.Pointer(&s))
                h.Len *= 8
                h.Cap *= 8
                return *(*[]byte)(unsafe.Pointer(h))
        case []float32:
                h := (*reflect.SliceHeader)(unsafe.Pointer(&s))
                h.Len *= 4
                h.Cap *= 4
                return *(*[]byte)(unsafe.Pointer(h))
        case []float64:
                h := (*reflect.SliceHeader)(unsafe.Pointer(&s))
                h.Len *= 8
                h.Cap *= 8
                return *(*[]byte)(unsafe.Pointer(h))
        default:
                panic(fmt.Sprintf("jsutil: unexpected value at sliceToBytesSlice: %T", s))
        }
}

func SliceToTypedArray(s interface{}) js.Value {
        switch s := s.(type) {
        case []int8:
                a := js.Global().Get("Uint8Array").New(len(s))
                js.CopyBytesToJS(a, sliceToByteSlice(s))
                runtime.KeepAlive(s)
                buf := a.Get("buffer")
                return js.Global().Get("Int8Array").New(buf, a.Get("byteOffset"), a.Get("byteLength"))
        case []int16:
                a := js.Global().Get("Uint8Array").New(len(s) * 2)
                js.CopyBytesToJS(a, sliceToByteSlice(s))
                runtime.KeepAlive(s)
                buf := a.Get("buffer")
                return js.Global().Get("Int16Array").New(buf, a.Get("byteOffset"), a.Get("byteLength").Int()/2)
        case []int32:
                a := js.Global().Get("Uint8Array").New(len(s) * 4)
                js.CopyBytesToJS(a, sliceToByteSlice(s))
                runtime.KeepAlive(s)
                buf := a.Get("buffer")
                return js.Global().Get("Int32Array").New(buf, a.Get("byteOffset"), a.Get("byteLength").Int()/4)
        case []uint8:
                a := js.Global().Get("Uint8Array").New(len(s))
                js.CopyBytesToJS(a, s)
                runtime.KeepAlive(s)
                return a
        case []uint16:
                a := js.Global().Get("Uint8Array").New(len(s) * 2)
                js.CopyBytesToJS(a, sliceToByteSlice(s))
                runtime.KeepAlive(s)
                buf := a.Get("buffer")
                return js.Global().Get("Uint16Array").New(buf, a.Get("byteOffset"), a.Get("byteLength").Int()/2)
        case []uint32:
                a := js.Global().Get("Uint8Array").New(len(s) * 4)
                js.CopyBytesToJS(a, sliceToByteSlice(s))
                runtime.KeepAlive(s)
                buf := a.Get("buffer")
                return js.Global().Get("Uint32Array").New(buf, a.Get("byteOffset"), a.Get("byteLength").Int()/4)
        case []float32:
                a := js.Global().Get("Uint8Array").New(len(s) * 4)
                js.CopyBytesToJS(a, sliceToByteSlice(s))
                runtime.KeepAlive(s)
                buf := a.Get("buffer")
                return js.Global().Get("Float32Array").New(buf, a.Get("byteOffset"), a.Get("byteLength").Int()/4)
        case []float64:
                a := js.Global().Get("Uint8Array").New(len(s) * 8)
                js.CopyBytesToJS(a, sliceToByteSlice(s))
                runtime.KeepAlive(s)
                buf := a.Get("buffer")
                return js.Global().Get("Float64Array").New(buf, a.Get("byteOffset"), a.Get("byteLength").Int()/8)
        default:
                panic(fmt.Sprintf("jsutil: unexpected value at SliceToTypedArray: %T", s))
        }
}
@johanbrandhorst

This comment has been minimized.

Copy link
Member

commented Jul 18, 2019

@hajimehoshi Is this available as a library somewhere?

@hajimehoshi

This comment has been minimized.

Copy link
Contributor Author

commented Jul 18, 2019

No so far. Ebiten has an internal package though: https://github.com/hajimehoshi/ebiten/tree/master/internal/jsutil

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants
You can’t perform that action at this time.