Search code examples
scalascala.js

Scala.js - Convert Uint8Array to Array[Byte]


How do I implement the following method in Scala.js?

import scala.scalajs.js

def toScalaArray(input: js.typedarray.Uint8Array): Array[Byte] =
  // code in question
  

Solution

  • All the current answers require iterating over the array in user space.

    Scala.js has optimizer supported conversions for typed arrays (in fact, Array[Byte] are typed arrays in modern configs). You'll likely get better performance by doing this:

    import scala.scalajs.js.typedarray._
    
    def toScalaArray(input: Uint8Array): Array[Byte] = {
      // Create a view as Int8 on the same underlying data.
      new Int8Array(input.buffer, input.byteOffset, input.length).toArray
    }
    

    The additional new Int8Array is necessary to re-interpret the underlying buffer as signed (the Byte type is signed). Only then, Scala.js will provide the built in conversion to Array[Byte].

    When looking at the generated code, you'll see no user space loop is necessary: The built-in slice method is used to copy the TypedArray. This will almost certainly not be beatable in terms of performance by any user-space loop.

    $c_Lhelloworld_HelloWorld$.prototype.toScalaArray__sjs_js_typedarray_Uint8Array__AB = (function(input) {
      var array = new Int8Array(input.buffer, $uI(input.byteOffset), $uI(input.length));
      return new $ac_B(array.slice())
    });
    

    If we compare this with the currently accepted answer (input.view.map(_.toByte).toArray) we see quite a difference (comments mine):

    $c_Lhelloworld_HelloWorld$.prototype.toScalaArray__sjs_js_typedarray_Uint8Array__AB = (function(input) {
      var this$2 = new $c_sjs_js_IterableOps(input);
      var this$5 = new $c_sc_IterableLike$$anon$1(this$2);
      // We need a function
      var f = new $c_sjsr_AnonFunction1(((x$1$2) => {
        var x$1 = $uS(x$1$2);
        return ((x$1 << 24) >> 24)
      }));
      new $c_sc_IterableView$$anon$1();
      // Here's the view: So indeed no intermediate allocations.
      var this$8 = new $c_sc_IterableViewLike$$anon$6(this$5, f);
      var len = $f_sc_TraversableOnce__size__I(this$8);
      var result = new $ac_B(len);
      // This function actually will traverse.
      $f_sc_TraversableOnce__copyToArray__O__I__V(this$8, result, 0);
      return result
    });