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
Open

Comments

@hajimehoshi
Copy link
Contributor

@hajimehoshi hajimehoshi 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

@dennwc dennwc 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

@hajimehoshi hajimehoshi 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

@johanbrandhorst johanbrandhorst commented Jul 18, 2019

@hajimehoshi Is this available as a library somewhere?

@hajimehoshi

This comment has been minimized.

Copy link
Contributor Author

@hajimehoshi hajimehoshi commented Jul 18, 2019

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

justinclift added a commit to justinclift/wasm-stl-viewer that referenced this issue Nov 6, 2019
Directly copied @hajimehoshi's workaround code from:

  golang/go#32402 (comment)

No idea if it'll work with TinyGo wasm yet, as it uses reflect.
Bluebugs pushed a commit to Bluebugs/gl that referenced this issue Feb 11, 2020
Bluebugs pushed a commit to Bluebugs/gl that referenced this issue Feb 12, 2020
Using information found in comment in golang/go#32402
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
4 participants
You can’t perform that action at this time.