1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2019 Google LLC
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#ifndef WasmCommon_DEFINED
9cb93a386Sopenharmony_ci#define WasmCommon_DEFINED
10cb93a386Sopenharmony_ci
11cb93a386Sopenharmony_ci#include <emscripten.h>
12cb93a386Sopenharmony_ci#include <emscripten/bind.h>
13cb93a386Sopenharmony_ci#include "include/core/SkColor.h"
14cb93a386Sopenharmony_ci#include "include/core/SkSpan.h"
15cb93a386Sopenharmony_ci#include "include/private/SkMalloc.h"
16cb93a386Sopenharmony_ci
17cb93a386Sopenharmony_ciusing namespace emscripten;
18cb93a386Sopenharmony_ci
19cb93a386Sopenharmony_ci// Self-documenting types
20cb93a386Sopenharmony_ciusing JSArray = emscripten::val;
21cb93a386Sopenharmony_ciusing JSObject = emscripten::val;
22cb93a386Sopenharmony_ciusing JSString = emscripten::val;
23cb93a386Sopenharmony_ciusing SkPathOrNull = emscripten::val;
24cb93a386Sopenharmony_ciusing TypedArray = emscripten::val;
25cb93a386Sopenharmony_ciusing Uint8Array = emscripten::val;
26cb93a386Sopenharmony_ciusing Uint16Array = emscripten::val;
27cb93a386Sopenharmony_ciusing Uint32Array = emscripten::val;
28cb93a386Sopenharmony_ciusing Float32Array = emscripten::val;
29cb93a386Sopenharmony_ci
30cb93a386Sopenharmony_ci// If we are using C++ and EMSCRIPTEN_BINDINGS, we can't have primitive pointers in our function
31cb93a386Sopenharmony_ci// type signatures. (this gives an error message like "Cannot call foo due to unbound
32cb93a386Sopenharmony_ci// types Pi, Pf").  But, we can just pretend they are numbers and cast them to be pointers and
33cb93a386Sopenharmony_ci// the compiler is happy.
34cb93a386Sopenharmony_ci// These types refer to the TypedArray that the JS interface wrote into or will read out of.
35cb93a386Sopenharmony_ci// This doesn't stop us from using these as different types; e.g. a float* can be treated as an
36cb93a386Sopenharmony_ci// SkPoint* in some APIs.
37cb93a386Sopenharmony_ciusing WASMPointerF32 = uintptr_t;
38cb93a386Sopenharmony_ciusing WASMPointerU8  = uintptr_t;
39cb93a386Sopenharmony_ciusing WASMPointerU16 = uintptr_t;
40cb93a386Sopenharmony_ciusing WASMPointerU32 = uintptr_t;
41cb93a386Sopenharmony_ciusing WASMPointer = uintptr_t;
42cb93a386Sopenharmony_ci
43cb93a386Sopenharmony_ci#define SPECIALIZE_JSARRAYTYPE(type, name)                  \
44cb93a386Sopenharmony_ci    template <> struct JSArrayType<type> {                  \
45cb93a386Sopenharmony_ci        static constexpr const char* const gName = name;    \
46cb93a386Sopenharmony_ci    }
47cb93a386Sopenharmony_ci
48cb93a386Sopenharmony_citemplate <typename T> struct JSArrayType {};
49cb93a386Sopenharmony_ci
50cb93a386Sopenharmony_ciSPECIALIZE_JSARRAYTYPE( int8_t,    "Int8Array");
51cb93a386Sopenharmony_ciSPECIALIZE_JSARRAYTYPE(uint8_t,   "Uint8Array");
52cb93a386Sopenharmony_ciSPECIALIZE_JSARRAYTYPE( int16_t,  "Int16Array");
53cb93a386Sopenharmony_ciSPECIALIZE_JSARRAYTYPE(uint16_t, "Uint16Array");
54cb93a386Sopenharmony_ciSPECIALIZE_JSARRAYTYPE( int32_t,  "Int32Array");
55cb93a386Sopenharmony_ciSPECIALIZE_JSARRAYTYPE(uint32_t, "Uint32Array");
56cb93a386Sopenharmony_ciSPECIALIZE_JSARRAYTYPE(float,   "Float32Array");
57cb93a386Sopenharmony_ci
58cb93a386Sopenharmony_ci#undef SPECIALIZE_JSARRAYTYPE
59cb93a386Sopenharmony_ci
60cb93a386Sopenharmony_ci/**
61cb93a386Sopenharmony_ci *  Create a typed-array (in the JS heap) and initialize it with the provided
62cb93a386Sopenharmony_ci *  data (from the wasm heap).
63cb93a386Sopenharmony_ci */
64cb93a386Sopenharmony_citemplate <typename T> TypedArray MakeTypedArray(int count, const T src[]) {
65cb93a386Sopenharmony_ci    emscripten::val length = emscripten::val(count);
66cb93a386Sopenharmony_ci    emscripten::val jarray = emscripten::val::global(JSArrayType<T>::gName).new_(count);
67cb93a386Sopenharmony_ci    jarray.call<void>("set", val(typed_memory_view(count, src)));
68cb93a386Sopenharmony_ci    return jarray;
69cb93a386Sopenharmony_ci}
70cb93a386Sopenharmony_ci
71cb93a386Sopenharmony_ci/**
72cb93a386Sopenharmony_ci *  Gives read access to a JSArray
73cb93a386Sopenharmony_ci *
74cb93a386Sopenharmony_ci *  We explicitly use malloc/free (not new/delete) so this can be used with allocations from the JS
75cb93a386Sopenharmony_ci *  side (ala CanvasKit.Malloc).
76cb93a386Sopenharmony_ci */
77cb93a386Sopenharmony_citemplate <typename T> class JSSpan {
78cb93a386Sopenharmony_cipublic:
79cb93a386Sopenharmony_ci    // Note: Use of this constructor is 5-20x slower than manually copying the data on the JS side
80cb93a386Sopenharmony_ci    // and sending over a pointer, length, and boolean for the other constructor.
81cb93a386Sopenharmony_ci    JSSpan(JSArray src) {
82cb93a386Sopenharmony_ci        const size_t len = src["length"].as<size_t>();
83cb93a386Sopenharmony_ci        T* data;
84cb93a386Sopenharmony_ci
85cb93a386Sopenharmony_ci        // If the buffer was allocated via CanvasKit' Malloc, we can peek directly at it!
86cb93a386Sopenharmony_ci        if (src["_ck"].isTrue()) {
87cb93a386Sopenharmony_ci            fOwned = false;
88cb93a386Sopenharmony_ci            data = reinterpret_cast<T*>(src["byteOffset"].as<size_t>());
89cb93a386Sopenharmony_ci        } else {
90cb93a386Sopenharmony_ci            fOwned = true;
91cb93a386Sopenharmony_ci            data = static_cast<T*>(sk_malloc_throw(len, sizeof(T)));
92cb93a386Sopenharmony_ci
93cb93a386Sopenharmony_ci            // now actually copy into 'data'
94cb93a386Sopenharmony_ci            if (src.instanceof(emscripten::val::global(JSArrayType<T>::gName))) {
95cb93a386Sopenharmony_ci                auto dst_view = emscripten::val(typed_memory_view(len, data));
96cb93a386Sopenharmony_ci                dst_view.call<void>("set", src);
97cb93a386Sopenharmony_ci            } else {
98cb93a386Sopenharmony_ci                for (size_t i = 0; i < len; ++i) {
99cb93a386Sopenharmony_ci                    data[i] = src[i].as<T>();
100cb93a386Sopenharmony_ci                }
101cb93a386Sopenharmony_ci            }
102cb93a386Sopenharmony_ci        }
103cb93a386Sopenharmony_ci        fSpan = SkSpan(data, len);
104cb93a386Sopenharmony_ci    }
105cb93a386Sopenharmony_ci
106cb93a386Sopenharmony_ci    JSSpan(WASMPointer ptr, size_t len, bool takeOwnership): fOwned(takeOwnership) {
107cb93a386Sopenharmony_ci        fSpan = SkSpan(reinterpret_cast<T*>(ptr), len);
108cb93a386Sopenharmony_ci    }
109cb93a386Sopenharmony_ci
110cb93a386Sopenharmony_ci    ~JSSpan() {
111cb93a386Sopenharmony_ci        if (fOwned) {
112cb93a386Sopenharmony_ci            sk_free(fSpan.data());
113cb93a386Sopenharmony_ci        }
114cb93a386Sopenharmony_ci    }
115cb93a386Sopenharmony_ci
116cb93a386Sopenharmony_ci    const T* data() const { return fSpan.data(); }
117cb93a386Sopenharmony_ci    size_t size() const { return fSpan.size(); }
118cb93a386Sopenharmony_ci
119cb93a386Sopenharmony_ciprivate:
120cb93a386Sopenharmony_ci    SkSpan<T>   fSpan;
121cb93a386Sopenharmony_ci    bool        fOwned;
122cb93a386Sopenharmony_ci};
123cb93a386Sopenharmony_ci
124cb93a386Sopenharmony_ci#endif
125