summaryrefslogtreecommitdiff
path: root/library/src/main/scala/scala/scalajs/js/ArrayOps.scala
blob: f7948ca092779ea15b9d84e8afc3599c3de149f4 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
/*                     __                                               *\
**     ________ ___   / /  ___      __ ____  Scala.js API               **
**    / __/ __// _ | / /  / _ | __ / // __/  (c) 2013, LAMP/EPFL        **
**  __\ \/ /__/ __ |/ /__/ __ |/_// /_\ \    http://scala-lang.org/     **
** /____/\___/_/ |_/____/_/ | |__/ /____/                               **
**                          |/____/                                     **
\*                                                                      */

package scala.scalajs.js

import scala.annotation.tailrec

import scala.collection.mutable
import mutable.Builder

/** Equivalent of scm.ArrayOps for js.Array */
@inline
final class ArrayOps[A](private[this] val array: Array[A])
    extends mutable.ArrayLike[A, Array[A]]
       with Builder[A, Array[A]] {

  import ArrayOps._

  /** Creates a new empty [[ArrayOps]]. */
  def this() = this(Array())

  // Implementation of ArrayLike

  @inline def apply(index: Int): A = array(index)
  @inline def length: Int = array.length
  @inline def update(index: Int, element: A): Unit = array(index) = element

  def seq: IndexedSeq[A] = new WrappedArray(array)

  override def repr: Array[A] = array

  override protected[this] def thisCollection: mutable.IndexedSeq[A] =
    toCollection(array)
  override protected[this] def toCollection(
      repr: Array[A]): mutable.IndexedSeq[A] = new WrappedArray(repr)

  protected[this] def newBuilder: Builder[A, Array[A]] =
    new ArrayOps[A]

  // Implementation of Builder

  @inline def +=(elem: A): this.type = {
    array.push(elem)
    this
  }

  @inline def clear(): Unit =
    array.length = 0

  @inline def result(): Array[A] = array

  // Scala notation for a fast concat()

  @inline def ++[B >: A](that: Array[_ <: B]): Array[B] =
    concat(array, that)

  // Methods whose inherited implementations do not play nice with the optimizer

  override def reduceLeft[B >: A](op: (B, A) => B): B = {
    val length = this.length
    if (length <= 0)
      throwUnsupported("empty.reduceLeft")

    @inline
    @tailrec
    def loop(start: Int, z: B): B =
      if (start == length) z
      else loop(start+1, op(z, this(start)))

    loop(1, this(0))
  }

  override def reduceRight[B >: A](op: (A, B) => B): B = {
    val length = this.length
    if (length <= 0)
      throwUnsupported("empty.reduceRight")

    @inline
    @tailrec
    def loop(end: Int, z: B): B =
      if (end == 0) z
      else loop(end-1, op(this(end-1), z))

    loop(length-1, this(length-1))
  }

}

object ArrayOps {

  /** Extract the throw in a separate, non-inlineable method. */
  private def throwUnsupported(msg: String): Nothing =
    throw new UnsupportedOperationException(msg)

  /** Non-inlined implementation of [[ArrayOps.++]]. */
  private def concat[A](left: Array[_ <: A], right: Array[_ <: A]): Array[A] = {
    val leftLength = left.length
    val rightLength = right.length
    val result = new Array[A](leftLength + rightLength)

    @inline
    @tailrec
    def loop(src: Array[_ <: A], i: Int, len: Int, offset: Int): Unit =
      if (i != len) {
        result(i+offset) = src(i)
        loop(src, i+1, len, offset)
      }

    loop(left, 0, leftLength, 0)
    loop(right, 0, rightLength, leftLength)
    result
  }

}